2023-10-17 16:29:33 -04:00

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);
}
}
}