using System; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework; namespace FSO.Common.Utils { public class TextureGenerator //a fun class for cpu generating textures { private static Texture2D PxWhite; private static Texture2D DefaultAdvLight; private static Texture2D PieButtonImg; private static Texture2D InteractionInactive; private static Texture2D InteractionActive; private static Texture2D CatalogInactive; private static Texture2D CatalogActive; private static Texture2D CatalogDisabled; private static Texture2D PieBG; private static Texture2D[] WallZBuffer; private static Texture2D[] AirTiles; private static Texture2D MotiveArrow; //actually a diamond, clip to get required direction private static Texture2D TerrainNoise; private static Texture2D UniformNoise; public static Texture2D GetPxWhite(GraphicsDevice gd) { if (PxWhite == null) PxWhite = TextureUtils.TextureFromColor(gd, Color.White); return PxWhite; } public static Texture2D GetDefaultAdv(GraphicsDevice gd) { if (DefaultAdvLight == null) DefaultAdvLight = TextureUtils.TextureFromColor(gd, new Color(128, 0, 0, 255)); //outdoors color max return DefaultAdvLight; } public static Texture2D GetPieButtonImg(GraphicsDevice gd) { if (PieButtonImg == null) PieButtonImg = GenerateRoundedRectangle(gd, new Color(0, 40, 140), 27, 27, 6); return PieButtonImg; } public static Texture2D GetInteractionActive(GraphicsDevice gd) { if (InteractionActive == null) InteractionActive = GenerateObjectIconBorder(gd, new Color(255, 255, 0), new Color(56, 88, 120)); return InteractionActive; } public static Texture2D GetInteractionInactive(GraphicsDevice gd) { if (InteractionInactive == null) InteractionInactive = GenerateObjectIconBorder(gd, new Color(128, 128, 128), new Color(56, 88, 120)); return InteractionInactive; } public static Texture2D GetCatalogInactive(GraphicsDevice gd) { if (CatalogInactive == null) CatalogInactive = GenerateCatalogIconBorder(gd, new Color(140, 170, 206), new Color(56, 88, 120)); return CatalogInactive; } public static Texture2D GetCatalogDisabled(GraphicsDevice gd) { if (CatalogDisabled == null) CatalogDisabled = GenerateCatalogIconBorder(gd, new Color(255, 0, 0), new Color(56, 88, 120)); return CatalogDisabled; } public static Texture2D GetCatalogActive(GraphicsDevice gd) { if (CatalogActive == null) CatalogActive = GenerateCatalogIconBorder(gd, new Color(140, 170, 206), new Color(189, 215, 247)); return CatalogActive; } public static Texture2D GetPieBG(GraphicsDevice gd) { if (PieBG == null) { PieBG = new Texture2D(gd, 200, 200); Color[] data = new Color[200 * 200]; int offset = 0; for (int y = 0; y < 200; y++) { for (int x = 0; x < 200; x++) { data[offset++] = new Color(0, 0, 0, (float)Math.Min(1, 2 - Math.Sqrt(Math.Pow(y - 100, 2) + Math.Pow(x - 100, 2)) / 50) * 0.5f); } } PieBG.SetData(data); } return PieBG; } public static Texture2D GetMotiveArrow(GraphicsDevice gd) { if (MotiveArrow == null) { MotiveArrow = new Texture2D(gd, 5, 5); Color[] data = new Color[5 * 5]; var size = new Vector2(5, 5); FillRect(data, size, new Rectangle(2, 0, 1, 1), Color.White); FillRect(data, size, new Rectangle(1, 1, 3, 1), Color.White); FillRect(data, size, new Rectangle(0, 2, 5, 1), Color.White); FillRect(data, size, new Rectangle(1, 3, 3, 1), Color.White); FillRect(data, size, new Rectangle(2, 4, 1, 1), Color.White); MotiveArrow.SetData(data); } return MotiveArrow; } public static Texture2D GetTerrainNoise(GraphicsDevice gd) { if (TerrainNoise == null) { TerrainNoise = new Texture2D(gd, 512, 512, true, SurfaceFormat.Color); Color[] data = new Color[512 * 512]; var rd = new Random(); for (int i = 0; i < data.Length; i++) { //distribution is an average of two noise functions. data[i].R = (byte)((rd.Next(255) + rd.Next(255)) / 2); data[i].G = (byte)((rd.Next(255) + rd.Next(255)) / 2); data[i].B = (byte)((rd.Next(255) + rd.Next(255)) / 2); data[i].A = (byte)((rd.Next(255) + rd.Next(255)) / 2); } TextureUtils.UploadWithMips(TerrainNoise, gd, data); } return TerrainNoise; } public static Texture2D GetUniformNoise(GraphicsDevice gd) { if (UniformNoise == null) { UniformNoise = new Texture2D(gd, 512, 512, true, SurfaceFormat.Color); Color[] data = new Color[512 * 512]; var rd = new Random(); for (int i = 0; i < data.Length; i++) { //distribution is an average of two noise functions. data[i].R = (byte)(rd.Next(255)); data[i].G = (byte)(rd.Next(255)); data[i].B = (byte)(rd.Next(255)); data[i].A = (byte)(rd.Next(255)); } TextureUtils.UploadWithMips(UniformNoise, gd, data); } return UniformNoise; } public static float FLAT_Z_INC = 1.525f; public static float[][] WallZBufferConfig = new float[][] { // format: width, height, startIntensity, Xdiff, Ydiff new float[] {64, 271, 74, 1, 0.5f}, //near top left new float[] {64, 271, 135, -1, 0.5f}, //near top right new float[] {128, 240, 89.5f, 0, 0.5f}, //near horiz diag new float[] {16, 232, 45, 0, 0.5f}, //near vert diag new float[] {32, 135, 74, 2, 1f}, //med top left new float[] {32, 135, 135, -2, 1f}, //med top right new float[] {64, 120, 89.5f, 0, 1f}, //med horiz diag new float[] {8, 116, 45, 0, 1f}, //med vert diag new float[] {16, 67, 74, 4, 2f}, //far top left new float[] {16, 67, 135, -4, 2f}, //far top right new float[] {32, 60, 89.5f, 0, 2f}, //far horiz diag new float[] {4, 58, 45, 0, 2f}, //far vert diag //12 new float[] {128, 64, 255, 0, -FLAT_Z_INC}, //near floor new float[] {64, 32, 255, 0, -FLAT_Z_INC*2}, //med floor new float[] {32, 16, 255, 0, -FLAT_Z_INC*4}, //far floor //vert flips of the above //15 new float[] {128, 64, 153, 0, FLAT_Z_INC}, new float[] {64, 32, 153, 0, FLAT_Z_INC*2}, new float[] {32, 16, 153, 0, FLAT_Z_INC*4}, //18 new float[] {128, 64, 263, 0, -FLAT_Z_INC}, //near junction walls up new float[] {64, 32, 263, 0, -FLAT_Z_INC*2}, //med junction walls up new float[] {32, 16, 263, 0, -FLAT_Z_INC*4}, //far junction walls up //versions for corners (man this is getting complicated) //21 //top corner new float[] {43, 22, 254, 0, -FLAT_Z_INC}, //near new float[] {21, 12, 254, 0, -FLAT_Z_INC*2}, //med new float[] {13, 7, 254, 0, -FLAT_Z_INC*4}, //far //24 //side corner new float[] {35, 21, 254 - (FLAT_Z_INC* 22), 0, -FLAT_Z_INC}, //near new float[] {16, 13, 254 - (FLAT_Z_INC * 22), 0, -FLAT_Z_INC*2}, //med new float[] {11, 8, 254 - (FLAT_Z_INC * 22), 0, -FLAT_Z_INC*4}, //far //27 new float[] {41, 23, 254 - (FLAT_Z_INC * (64 - 23)), 0, -FLAT_Z_INC}, //near new float[] {18, 13, 254 - (FLAT_Z_INC * (64 - 23)), 0, -FLAT_Z_INC*2}, //med new float[] {9, 8, 254 - (FLAT_Z_INC * (64 - 23)), 0, -FLAT_Z_INC*4}, //far //30 new float[] {1, 1, 49, 0, 0} //balloon }; public static Texture2D[] GetWallZBuffer(GraphicsDevice gd) { float bias = 0f; if (WallZBuffer == null) { var count = WallZBufferConfig.Length; WallZBuffer = new Texture2D[count]; for (int i = 0; i < count; i++) { var config = WallZBufferConfig[i]; int width = (int)config[0]; int height = (int)config[1]; WallZBuffer[i] = new Texture2D(gd, width, height); Color[] data = new Color[width * height]; int offset = 0; float yInt = config[2]; for (int y = 0; y < height; y++) { float xInt = yInt; for (int x = 0; x < width; x++) { byte zCol = (byte)Math.Round(Math.Min(255, xInt + bias)); data[offset++] = new Color(zCol, zCol, zCol, zCol); xInt += config[3]; } yInt += config[4]; } WallZBuffer[i].SetData(data); } } return WallZBuffer; } public static Texture2D[] GetAirTiles(GraphicsDevice gd) { if (AirTiles == null) { AirTiles = new Texture2D[3]; AirTiles[0] = GenerateAirTile(gd, 127, 64); AirTiles[1] = GenerateAirTile(gd, 63, 32); AirTiles[2] = GenerateAirTile(gd, 31, 16); } return AirTiles; } private static Texture2D GenerateAirTile(GraphicsDevice gd, int width, int height) { var tex = new Texture2D(gd, width+1, height); Color[] data = new Color[width * height]; int center = width/2; int middleOff = 0; for (int i=0; i height / 2)?Color.Black:Color.White; for (int j = 0; j < (middleOff * 2) - 3; j++) if (i % 2 == 0 && (i + (center - middleOff)+j) % 4 == 0) data[index++] = Color.Black; else index++; if (middleOff != 0) { for (int j = 0; j < 2; j++) data[index++] = (i + (1-j) > height / 2) ? Color.Black : Color.White; } middleOff += (i == height/2-1)?1:((i(FloorCopy(data, width, height)); return tex; } public static Color[] FloorCopy(Color[] data, int width, int height) { if (width % 2 != 0) { var target = new Color[(width + 1) * height]; for (int y = 0; y < height; y++) { Array.Copy(data, y * width, target, y * (width + 1), width); } data = target; width += 1; } var ndat = new Color[data.Length]; int hw = (width) / 2; int hh = (height) / 2; int idx = 0; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { var xp = (x + hw) % width; var yp = (y + hh) % height; var rep = data[xp + yp * width]; if (rep.A >= 254) ndat[idx] = rep; else ndat[idx] = data[idx]; idx++; } } return ndat; } public static Texture2D GenerateObjectIconBorder(GraphicsDevice gd, Color highlight, Color bg) { var tex = new Texture2D(gd, 45, 45); Color[] data = new Color[45*45]; var size = new Vector2(45, 45); //border FillRect(data, size, new Rectangle(3, 0, 39, 2), highlight); FillRect(data, size, new Rectangle(0, 3, 2, 39), highlight); FillRect(data, size, new Rectangle(3, 43, 39, 2), highlight); FillRect(data, size, new Rectangle(43, 3, 2, 39), highlight); //end border //bg FillRect(data, size, new Rectangle(2, 2, 41, 41), bg); //end bg //top left rounded FillRect(data, size, new Rectangle(2, 1, 2, 2), highlight); FillRect(data, size, new Rectangle(1, 2, 2, 2), highlight); //top right rounded FillRect(data, size, new Rectangle(41, 1, 2, 2), highlight); FillRect(data, size, new Rectangle(42, 2, 2, 2), highlight); //btm left rounded FillRect(data, size, new Rectangle(1, 41, 2, 2), highlight); FillRect(data, size, new Rectangle(2, 42, 2, 2), highlight); //btm right rounded FillRect(data, size, new Rectangle(41, 42, 2, 2), highlight); FillRect(data, size, new Rectangle(42, 41, 2, 2), highlight); tex.SetData(data); return tex; } public static Texture2D GenerateCatalogIconBorder(GraphicsDevice gd, Color highlight, Color bg) { var tex = new Texture2D(gd, 41, 41); Color[] data = new Color[41 * 41]; var size = new Vector2(41, 41); //border FillRect(data, size, new Rectangle(2, 0, 37, 1), highlight); FillRect(data, size, new Rectangle(0, 2, 1, 37), highlight); FillRect(data, size, new Rectangle(2, 40, 37, 1), highlight); FillRect(data, size, new Rectangle(40, 2, 1, 37), highlight); //end border //bg FillRect(data, size, new Rectangle(1, 1, 39, 39), bg); //end bg //top left rounded FillRect(data, size, new Rectangle(1, 1, 1, 1), highlight); //top right rounded FillRect(data, size, new Rectangle(39, 1, 1, 1), highlight); //btm left rounded FillRect(data, size, new Rectangle(1, 39, 1, 1), highlight); //btm right rounded FillRect(data, size, new Rectangle(39, 39, 1, 1), highlight); tex.SetData(data); return tex; } public static Texture2D GenerateRoundedRectangle(GraphicsDevice gd, Color color, int width, int height, int radius) { var tex = new Texture2D(gd, width, height); Color[] data = new Color[width * height]; var size = new Vector2(width, height); //rect fills FillRect(data, size, new Rectangle(radius, radius, width - radius * 2, height - radius * 2), color); FillRect(data, size, new Rectangle(radius, 0, width - radius * 2, radius), color); FillRect(data, size, new Rectangle(radius, height-radius, width - radius * 2, radius), color); FillRect(data, size, new Rectangle(0, radius, radius, height-radius*2), color); FillRect(data, size, new Rectangle(width - radius, radius, radius, height - radius * 2), color); //corners now for (int i = 0; i < radius; i++) { int seg = (int)Math.Round(Math.Sin(Math.Acos((radius-(i+0.5))/radius))*radius); FillRect(data, size, new Rectangle(radius-seg, i, seg, 1), color); FillRect(data, size, new Rectangle(width-radius, i, seg, 1), color); FillRect(data, size, new Rectangle(radius - seg, height - i - 1, seg, 1), color); FillRect(data, size, new Rectangle(width-radius, height - i - 1, seg, 1), color); } tex.SetData(data); return tex; } private static void FillRect(Color[] data, Vector2 texSize, Rectangle dest, Color fillColor) { int x; int y=dest.Y; for (int i = 0; i < dest.Height; i++) { x = dest.X; for (int j = 0; j < dest.Width; j++) { data[y * (int)texSize.X + x] = fillColor; x++; } y++; } } private static Texture2D Sun; public static Texture2D GetSun(GraphicsDevice gd) { if (Sun == null) { Sun = new Texture2D(gd, 256, 256); Color[] data = new Color[256 * 256]; int offset = 0; for (int y = 0; y < 256; y++) { for (int x = 0; x < 256; x++) { var distance = Math.Sqrt((y - 128) * (y - 128) + (x - 128) * (x - 128)); var intensity = (1 - (distance - 25) / 103f); if (intensity < 0) data[offset++] = Color.Transparent; else { intensity *= intensity; data[offset++] = new Color(1, 1, 1, (float)intensity); } } } Sun.SetData(data); } return Sun; } private static Texture2D Moon; public static Texture2D GetMoon(GraphicsDevice gd) { if (Moon == null) { Moon = new Texture2D(gd, 64, 64); Color[] data = new Color[64 * 64]; int offset = 0; for (int y = 0; y < 64; y++) { for (int x = 0; x < 64; x++) { var distance = Math.Sqrt((y - 32) * (y - 32) + (x - 32) * (x - 32)); var intensity = Math.Min(1, Math.Max(0, 32 - distance)); if (intensity > 0) { //calculate crescent if (x < 32) distance = 0; else distance = Math.Sqrt((y - 32) * (y - 32) + (x - 32)*2 * (x - 32)*2); intensity *= 0.2f+(1-Math.Min(1, Math.Max(0, 32 - distance)))*0.8f; } data[offset++] = new Color(1, 1, 1, (float)intensity); } } Moon.SetData(data); } return Moon; } public static Color FromHSV(float h, float s, float v) { var h2 = (int)h / 60; var chroma = s * v; //times value, but it is always one var X = chroma * (1 - Math.Abs(((h / 60f) % 2) - 1)); Color result; switch (h2) { case 0: result = new Color(chroma, X, 0); break; case 1: result = new Color(X, chroma, 0); break; case 2: result = new Color(0, chroma, X); break; case 3: result = new Color(0, X, chroma); break; case 4: result = new Color(X, 0, chroma); break; case 5: result = new Color(chroma, 0, X); break; default: result = Color.Black; break; //undefined } var m = v - chroma; var blend = Color.White * m; result.R += blend.R; result.G += blend.G; result.B += blend.B; return result; } public static Tuple ToHSV(Color color) { var r = color.R / 255f; var g = color.G / 255f; var b = color.B / 255f; var min = Math.Min(Math.Min(r, g), b); var max = Math.Max(Math.Max(r, g), b); if (min == max) return new Tuple(0, 0, min); var d = (r == min) ? (g - b) : ((b == min) ? r - g : b - r); var h = (r == min) ? 3 : ((b == min) ? 1 : 5); return new Tuple( 60 * (h - d / (max - min)), (max - min) / max, max); } private static Texture2D HSMatrix; public static Texture2D GetHSMatrix(GraphicsDevice gd) { if (HSMatrix == null) { HSMatrix = new Texture2D(gd, 360, 256); Color[] data = new Color[360 * 256]; int offset = 0; for (int y = 0; y < 256; y++) //y is saturation { for (int x = 0; x < 360; x++) //x is hue { data[offset++] = FromHSV(x, 1 - (y / 256f), 1f); } } HSMatrix.SetData(data); } return HSMatrix; } private static Texture2D HSGrad; public static Texture2D GetHSGrad(GraphicsDevice gd) { if (HSGrad == null) { HSGrad = new Texture2D(gd, 1, 256); Color[] data = new Color[1 * 256]; int offset = 0; for (int y = 0; y < 256; y++) //y is saturation { var mod = Color.White * (1 - y / 255f); mod.A = 255; data[offset++] = mod; } HSGrad.SetData(data); } return HSGrad; } } }