296 lines
10 KiB
C#
Executable File
296 lines
10 KiB
C#
Executable File
using OpenTK.Graphics.OpenGL4;
|
|
using OpenTK.Mathematics;
|
|
using SharpFont;
|
|
using System.Reflection;
|
|
using Encoding = SharpFont.Encoding;
|
|
|
|
namespace Updater;
|
|
|
|
internal class RenderText : IRenderObject
|
|
{
|
|
public static readonly Shader DefaultTextShader = new(@"#version 460
|
|
|
|
layout (location = 0) in vec2 in_pos;
|
|
layout (location = 1) in vec2 in_uv;
|
|
|
|
out vec2 vUV;
|
|
|
|
layout (location = 0) uniform mat4 model;
|
|
layout (location = 1) uniform mat4 projection;
|
|
|
|
void main()
|
|
{
|
|
vUV = in_uv.xy;
|
|
gl_Position = projection * model * vec4(in_pos.xy, 0.0, 1.0);
|
|
}",
|
|
@"
|
|
#version 460
|
|
|
|
in vec2 vUV;
|
|
|
|
layout (binding=0) uniform sampler2D u_texture;
|
|
|
|
layout (location = 2) uniform vec4 textColor;
|
|
|
|
out vec4 fragColor;
|
|
|
|
void main()
|
|
{
|
|
vec2 uv = vUV.xy;
|
|
float text = texture(u_texture, uv).r;
|
|
fragColor = vec4(textColor.rgba*text);
|
|
}");
|
|
|
|
public RenderText(string Text, string Font, uint PixelHeight, float x, float y, float scale, Vector2 dir, Vector4 col)
|
|
{
|
|
this.Text = Text;
|
|
this.PixelHeight = PixelHeight;
|
|
this.X = FloatToInt(x, 1);
|
|
this.Y = FloatToInt(y, 1, true);
|
|
this.Scale = scale;
|
|
this.DIR = dir;
|
|
this.Color = col;
|
|
this.Font = Font;
|
|
|
|
|
|
|
|
|
|
Library lib = new();
|
|
|
|
Assembly assembly = Assembly.GetExecutingAssembly();
|
|
Stream? resource_stream = assembly.GetManifestResourceStream($"Updater.Resource.{Font}.ttf");
|
|
MemoryStream ms = new();
|
|
resource_stream?.CopyTo(ms);
|
|
Face face = new(lib, ms.ToArray(), 0);
|
|
face.SetPixelSizes(0, PixelHeight);
|
|
GL.PixelStore(PixelStoreParameter.UnpackAlignment, 1);
|
|
GL.ActiveTexture(TextureUnit.Texture0);
|
|
face.SelectCharmap(Encoding.Unicode);
|
|
|
|
for (uint c = 0; c < 0; c++)
|
|
{
|
|
try
|
|
{
|
|
face.LoadChar(c, LoadFlags.Render, LoadTarget.Normal);
|
|
GlyphSlot glyph = face.Glyph;
|
|
FTBitmap bitmap = glyph.Bitmap;
|
|
int texObj = GL.GenTexture();
|
|
GL.BindTexture(TextureTarget.Texture2D, texObj);
|
|
GL.TexImage2D(TextureTarget.Texture2D, 0,
|
|
PixelInternalFormat.R8, bitmap.Width, bitmap.Rows, 0,
|
|
PixelFormat.Red, PixelType.UnsignedByte, bitmap.Buffer);
|
|
|
|
GL.TextureParameter(texObj, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
|
|
GL.TextureParameter(texObj, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
|
|
GL.TextureParameter(texObj, TextureParameterName.TextureWrapS, (int)TextureWrapMode.ClampToEdge);
|
|
GL.TextureParameter(texObj, TextureParameterName.TextureWrapT, (int)TextureWrapMode.ClampToEdge);
|
|
|
|
Character ch = new();
|
|
ch.TextureID = texObj;
|
|
ch.Size = new Vector2(bitmap.Width, bitmap.Rows);
|
|
ch.Bearing = new Vector2(glyph.BitmapLeft, glyph.BitmapTop);
|
|
ch.Advance = (int)glyph.Advance.X.Value;
|
|
if (!_characters.ContainsKey(this)) _characters.Add(this, new Dictionary<uint, Character>());
|
|
_characters[this].Add(c, ch);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.WriteLine(ex);
|
|
}
|
|
}
|
|
|
|
GL.BindTexture(TextureTarget.Texture2D, 0);
|
|
|
|
GL.PixelStore(PixelStoreParameter.UnpackAlignment, 4);
|
|
|
|
float[] vquad =
|
|
{
|
|
0.0f, -1.0f, 0.0f, 0.0f,
|
|
0.0f, 0.0f, 0.0f, 1.0f,
|
|
1.0f, 0.0f, 1.0f, 1.0f,
|
|
0.0f, -1.0f, 0.0f, 0.0f,
|
|
1.0f, 0.0f, 1.0f, 1.0f,
|
|
1.0f, -1.0f, 1.0f, 0.0f
|
|
};
|
|
|
|
VBO = GL.GenBuffer();
|
|
GL.BindBuffer(BufferTarget.ArrayBuffer, VBO);
|
|
GL.BufferData(BufferTarget.ArrayBuffer, 4 * 6 * 4, vquad, BufferUsageHint.StaticDraw);
|
|
|
|
VAO = GL.GenVertexArray();
|
|
GL.BindVertexArray(VAO);
|
|
GL.EnableVertexAttribArray(0);
|
|
GL.VertexAttribPointer(0, 2, VertexAttribPointerType.Float, false, 4 * 4, 0);
|
|
GL.EnableVertexAttribArray(1);
|
|
GL.VertexAttribPointer(1, 2, VertexAttribPointerType.Float, false, 4 * 4, 2 * 4);
|
|
|
|
GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
|
|
GL.BindVertexArray(0);
|
|
}
|
|
|
|
|
|
public bool Visible { get; set; } = true;
|
|
|
|
public static readonly Dictionary<RenderText, Dictionary<uint, Character>> _characters = new();
|
|
|
|
public int VAO { get; set; }
|
|
public int VBO { get; set; }
|
|
public float X { get; set; }
|
|
public float Y { get; set; }
|
|
public Vector2 DIR { get; set; }
|
|
public string Text { get; set; }
|
|
public uint PixelHeight { get; set; }
|
|
public float Scale { get; set; }
|
|
public Shader Shader { get; } = DefaultTextShader;
|
|
private string Font { get; set; }
|
|
|
|
private Vector4 Color { get; set; }
|
|
|
|
public void Clean()
|
|
{
|
|
|
|
}
|
|
|
|
private static float FloatToInt(float p, int Size, bool Invert = false)
|
|
{
|
|
double half = Math.Round((double)Size / (double)2, 15);
|
|
if (p == 0) return (int)half;
|
|
if (Invert)
|
|
{
|
|
if (p < 0)
|
|
{
|
|
p *= -1;
|
|
p++;
|
|
return float.Parse($"{(half * p)}");
|
|
}
|
|
else
|
|
{
|
|
return float.Parse($"{half - (p * half)}");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (p < 0)
|
|
{
|
|
p *= -1;
|
|
p++;
|
|
return float.Parse($"{Size - (half * p)}");
|
|
}
|
|
else
|
|
{
|
|
return float.Parse($"{p * half + half}");
|
|
}
|
|
}
|
|
}
|
|
|
|
public void Draw()
|
|
{
|
|
if (Visible)
|
|
{
|
|
Shader.Use();
|
|
GL.Enable(EnableCap.Blend);
|
|
GL.Uniform4(2, Color);
|
|
_ = Matrix4.CreateScale(new Vector3(1f / 1, 1f / 1, 1.0f));
|
|
Matrix4 projectionM = Matrix4.CreateOrthographicOffCenter(0.0f, 1, 1, 0.0f, -1.0f, 1.0f);
|
|
GL.BlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.OneMinusSrcAlpha);
|
|
GL.BlendFunc(0, BlendingFactorSrc.SrcAlpha, BlendingFactorDest.OneMinusSrcAlpha);
|
|
GL.UniformMatrix4(1, false, ref projectionM);
|
|
GL.ActiveTexture(TextureUnit.Texture0);
|
|
GL.BindVertexArray(VAO);
|
|
|
|
float angle_rad = (float)Math.Atan2(DIR.Y, DIR.X);
|
|
Matrix4 rotateM = Matrix4.CreateRotationZ(angle_rad);
|
|
Matrix4 transOriginM = Matrix4.CreateTranslation(new Vector3(X, Y, 0f));
|
|
|
|
// Iterate through all characters
|
|
float char_x = 0.0f;
|
|
|
|
|
|
Library lib = new();
|
|
|
|
Assembly assembly = Assembly.GetExecutingAssembly();
|
|
Stream? resource_stream = assembly.GetManifestResourceStream($"Updater.Resource.{Font}.ttf");
|
|
MemoryStream ms = new();
|
|
resource_stream?.CopyTo(ms);
|
|
Face face = new(lib, ms.ToArray(), 0);
|
|
|
|
face.SetPixelSizes(0, PixelHeight);
|
|
|
|
// set 1 byte pixel alignment
|
|
GL.PixelStore(PixelStoreParameter.UnpackAlignment, 1);
|
|
|
|
// set texture unit
|
|
GL.ActiveTexture(TextureUnit.Texture0);
|
|
face.SelectCharmap(Encoding.Unicode);
|
|
|
|
|
|
|
|
foreach (char c in Text)
|
|
{
|
|
if (_characters.ContainsKey(this) == false || _characters[this].ContainsKey(c) == false)
|
|
{
|
|
var temp = ((ushort)c);
|
|
|
|
try
|
|
{
|
|
face.LoadChar(temp, LoadFlags.Render, LoadTarget.Normal);
|
|
GlyphSlot glyph = face.Glyph;
|
|
FTBitmap bitmap = glyph.Bitmap;
|
|
|
|
int texObj = GL.GenTexture();
|
|
GL.BindTexture(TextureTarget.Texture2D, texObj);
|
|
GL.TexImage2D(TextureTarget.Texture2D, 0,
|
|
PixelInternalFormat.R8, bitmap.Width, bitmap.Rows, 0,
|
|
PixelFormat.Red, PixelType.UnsignedByte, bitmap.Buffer);
|
|
|
|
// set texture parameters
|
|
GL.TextureParameter(texObj, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
|
|
GL.TextureParameter(texObj, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
|
|
GL.TextureParameter(texObj, TextureParameterName.TextureWrapS, (int)TextureWrapMode.ClampToEdge);
|
|
GL.TextureParameter(texObj, TextureParameterName.TextureWrapT, (int)TextureWrapMode.ClampToEdge);
|
|
|
|
// add character
|
|
Character cha = new();
|
|
cha.TextureID = texObj;
|
|
cha.Size = new Vector2(bitmap.Width, bitmap.Rows);
|
|
cha.Bearing = new Vector2(glyph.BitmapLeft, glyph.BitmapTop);
|
|
cha.Advance = (int)glyph.Advance.X.Value;
|
|
if (!_characters.ContainsKey(this)) _characters.Add(this, new Dictionary<uint, Character>());
|
|
_characters[this].Add(temp, cha);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.WriteLine(ex);
|
|
}
|
|
}
|
|
|
|
Character ch = _characters[this][c];
|
|
|
|
float w = ch.Size.X * Scale;
|
|
float h = ch.Size.Y * Scale;
|
|
float xrel = char_x + ch.Bearing.X * Scale;
|
|
float yrel = (ch.Size.Y - ch.Bearing.Y) * Scale;
|
|
|
|
// Now advance cursors for next glyph (note that advance is number of 1/64 pixels)
|
|
char_x += (ch.Advance >> 6) * Scale; // Bitshift by 6 to get value in pixels (2^6 = 64 (divide amount of 1/64th pixels by 64 to get amount of pixels))
|
|
|
|
Matrix4 scaleM = Matrix4.CreateScale(new Vector3(w, h, 1.0f));
|
|
Matrix4 transRelM = Matrix4.CreateTranslation(new Vector3(xrel, yrel, 0.0f));
|
|
|
|
Matrix4 modelM = scaleM * transRelM * rotateM * transOriginM; // OpenTK `*`-operator is reversed
|
|
GL.UniformMatrix4(0, false, ref modelM);
|
|
|
|
// Render glyph texture over quad
|
|
GL.BindTexture(TextureTarget.Texture2D, ch.TextureID);
|
|
|
|
// Render quad
|
|
GL.DrawArrays(PrimitiveType.Triangles, 0, 6);
|
|
}
|
|
|
|
GL.BindVertexArray(0);
|
|
GL.BindTexture(TextureTarget.Texture2D, 0);
|
|
GL.Disable(EnableCap.Blend);
|
|
}
|
|
}
|
|
}
|