mysimulation/server/tso.common/Utils/TextureGenerator.cs
Tony Bark 22191ce648 Removed NioTSO client and server
- NioTSO client isn't needed because we're using RayLib
- Added FreeSO's API server to handle most backend operations
2024-05-01 02:55:43 -04:00

582 lines
22 KiB
C#
Executable file

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<Color>(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<Color>(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<Color>(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; i++)
{
int index = i * width + (center - middleOff);
for (int j=0; j<((middleOff==0)?1:2); j++)
data[index++] = (i+j > 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<height/2)?2:-2);
}
tex.SetData<Color>(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<Color>(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<Color>(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<Color>(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<Color>(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<Color>(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<float, float, float> 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<float, float, float>(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<float, float, float>(
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<Color>(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<Color>(data);
}
return HSGrad;
}
}
}