2024-01-19 23:55:07 +02:00
|
|
|
package zephr
|
|
|
|
|
|
|
|
|
|
import "core:fmt"
|
2024-01-21 03:42:06 +02:00
|
|
|
import "core:log"
|
2024-01-19 23:55:07 +02:00
|
|
|
|
|
|
|
|
import gl "vendor:OpenGL"
|
|
|
|
|
|
|
|
|
|
import FT "../freetype"
|
|
|
|
|
|
|
|
|
|
Character :: struct {
|
|
|
|
|
size: Vec2,
|
|
|
|
|
bearing: Vec2,
|
|
|
|
|
advance: u32,
|
|
|
|
|
tex_coords: [4]Vec2,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Font :: struct {
|
|
|
|
|
atlas_tex_id: TextureId,
|
|
|
|
|
characters: [128]Character,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// GlyphInstance needs to be 100 bytes (meaning no padding) otherwise the data we send to the shader
|
|
|
|
|
// is misaligned and we get rendering errors
|
|
|
|
|
#assert(size_of(GlyphInstance) == 100)
|
|
|
|
|
|
|
|
|
|
GlyphInstance :: struct #packed {
|
|
|
|
|
pos: Vec4,
|
|
|
|
|
tex_coords_idx: u32,
|
|
|
|
|
color: Vec4,
|
|
|
|
|
model: Mat4,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
GlyphInstanceList :: [dynamic]GlyphInstance
|
|
|
|
|
|
|
|
|
|
@private
|
|
|
|
|
FONT_PIXEL_SIZE :: 100
|
|
|
|
|
@private
|
|
|
|
|
LINE_HEIGHT :: 2.0
|
|
|
|
|
|
|
|
|
|
@private
|
|
|
|
|
font_shader : Shader
|
|
|
|
|
@private
|
|
|
|
|
font_vao : u32
|
|
|
|
|
@private
|
|
|
|
|
font_instance_vbo : u32
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
init_freetype :: proc(font_path: cstring) -> i32 {
|
2024-01-21 03:42:06 +02:00
|
|
|
context.logger = logger
|
|
|
|
|
|
2024-01-20 06:39:49 +02:00
|
|
|
ft: FT.Library
|
2024-01-19 23:55:07 +02:00
|
|
|
if (FT.Init_FreeType(&ft) != 0) {
|
2024-01-20 06:39:49 +02:00
|
|
|
return -1
|
2024-01-19 23:55:07 +02:00
|
|
|
}
|
|
|
|
|
|
2024-01-20 06:39:49 +02:00
|
|
|
face: FT.Face
|
2024-01-19 23:55:07 +02:00
|
|
|
if (FT.New_Face(ft, font_path, 0, &face) != 0) {
|
2024-01-20 06:39:49 +02:00
|
|
|
return -2
|
2024-01-19 23:55:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// sets the variable font to be bold
|
|
|
|
|
/* if ((face->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS)) { */
|
|
|
|
|
/* printf("[INFO] Got a variable font\n"); */
|
|
|
|
|
/* FT_MM_Var *mm; */
|
|
|
|
|
/* FT_Get_MM_Var(face, &mm); */
|
|
|
|
|
|
|
|
|
|
/* FT_Set_Var_Design_Coordinates(face, mm->num_namedstyles, mm->namedstyle[mm->num_namedstyles - 4].coords); */
|
|
|
|
|
|
|
|
|
|
/* FT_Done_MM_Var(ft, mm); */
|
|
|
|
|
/* } */
|
|
|
|
|
|
2024-01-20 06:39:49 +02:00
|
|
|
FT.Set_Pixel_Sizes(face, 0, FONT_PIXEL_SIZE)
|
2024-01-19 23:55:07 +02:00
|
|
|
|
2024-01-20 06:39:49 +02:00
|
|
|
gl.PixelStorei(gl.UNPACK_ALIGNMENT, 1)
|
2024-01-19 23:55:07 +02:00
|
|
|
|
|
|
|
|
pen_x, pen_y: u32
|
|
|
|
|
|
|
|
|
|
/* FT_UInt glyph_idx; */
|
|
|
|
|
/* FT_ULong c = FT_Get_First_Char(face, &glyph_idx); */
|
|
|
|
|
|
2024-01-20 06:39:49 +02:00
|
|
|
tex_width, tex_height: u32
|
2024-01-19 23:55:07 +02:00
|
|
|
for i in 32..<128 {
|
|
|
|
|
if (FT.Load_Char(face, cast(u64)i, .RENDER) != 0) {
|
2024-01-21 03:42:06 +02:00
|
|
|
log.errorf("Failed to load glyph for char '0x%x'", i)
|
2024-01-19 23:55:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* FT_Render_Glyph(face->glyph, FT_RENDER_MODE_SDF); */
|
|
|
|
|
|
2024-01-20 06:39:49 +02:00
|
|
|
tex_width += face.glyph.bitmap.width + 1
|
|
|
|
|
tex_height = max(tex_height, face.glyph.bitmap.rows)
|
2024-01-19 23:55:07 +02:00
|
|
|
}
|
|
|
|
|
|
2024-01-20 06:39:49 +02:00
|
|
|
pixels := make([dynamic]u8, tex_width * tex_height)
|
2024-01-19 23:55:07 +02:00
|
|
|
defer delete(pixels)
|
|
|
|
|
|
|
|
|
|
for i in 32..<128 {
|
|
|
|
|
/* while (glyph_idx) { */
|
|
|
|
|
if (FT.Load_Char(face, cast(u64)i, .RENDER) != 0) {
|
2024-01-21 03:42:06 +02:00
|
|
|
log.errorf("Failed to load glyph for char '0x%x'", i)
|
2024-01-19 23:55:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* FT_Render_Glyph(face->glyph, FT_RENDER_MODE_SDF); */
|
|
|
|
|
|
2024-01-20 06:39:49 +02:00
|
|
|
bmp := &face.glyph.bitmap
|
2024-01-19 23:55:07 +02:00
|
|
|
|
|
|
|
|
if (pen_x + bmp.width >= tex_width) {
|
2024-01-20 06:39:49 +02:00
|
|
|
pen_x = 0
|
|
|
|
|
pen_y += cast(u32)(1 + (face.size.metrics.height >> 6))
|
2024-01-19 23:55:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for row in 0..<bmp.rows {
|
|
|
|
|
for col in 0..<bmp.width {
|
2024-01-20 06:39:49 +02:00
|
|
|
x := pen_x + col
|
|
|
|
|
y := pen_y + row
|
|
|
|
|
pixels[y * tex_width + x] = bmp.buffer[row * cast(u32)bmp.pitch + col]
|
2024-01-19 23:55:07 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-20 06:39:49 +02:00
|
|
|
atlas_x0 := cast(f32)pen_x / cast(f32)tex_width
|
|
|
|
|
atlas_y0 := cast(f32)pen_y / cast(f32)tex_height
|
|
|
|
|
atlas_x1 := cast(f32)(pen_x + bmp.width) / cast(f32)tex_width
|
|
|
|
|
atlas_y1 := cast(f32)(pen_y + bmp.rows) / cast(f32)tex_height
|
2024-01-19 23:55:07 +02:00
|
|
|
|
2024-01-20 06:39:49 +02:00
|
|
|
top_left := Vec2{ atlas_x0, atlas_y1 }
|
|
|
|
|
top_right := Vec2{ atlas_x1, atlas_y1 }
|
|
|
|
|
bottom_right := Vec2{ atlas_x1, atlas_y0 }
|
|
|
|
|
bottom_left := Vec2{ atlas_x0, atlas_y0 }
|
2024-01-19 23:55:07 +02:00
|
|
|
|
|
|
|
|
character: Character
|
|
|
|
|
|
2024-01-20 06:39:49 +02:00
|
|
|
character.tex_coords[0] = top_left
|
|
|
|
|
character.tex_coords[1] = top_right
|
|
|
|
|
character.tex_coords[2] = bottom_right
|
|
|
|
|
character.tex_coords[3] = bottom_left
|
|
|
|
|
character.advance = cast(u32)face.glyph.advance.x
|
|
|
|
|
character.size = Vec2{ cast(f32)face.glyph.bitmap.width, cast(f32)face.glyph.bitmap.rows }
|
|
|
|
|
character.bearing = Vec2{ cast(f32)face.glyph.bitmap_left, cast(f32)face.glyph.bitmap_top }
|
2024-01-19 23:55:07 +02:00
|
|
|
|
2024-01-20 06:39:49 +02:00
|
|
|
zephr_ctx.font.characters[i] = character
|
2024-01-19 23:55:07 +02:00
|
|
|
|
2024-01-20 06:39:49 +02:00
|
|
|
pen_x += bmp.width + 1
|
2024-01-19 23:55:07 +02:00
|
|
|
|
|
|
|
|
/* c = FT_Get_Next_Char(face, c, &glyph_idx); */
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-20 06:39:49 +02:00
|
|
|
texture: TextureId
|
|
|
|
|
gl.GenTextures(1, &texture)
|
|
|
|
|
gl.BindTexture(gl.TEXTURE_2D, texture)
|
2024-01-19 23:55:07 +02:00
|
|
|
gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RED, cast(i32)tex_width,
|
|
|
|
|
cast(i32)tex_height, 0, gl.RED, gl.UNSIGNED_BYTE,
|
|
|
|
|
raw_data(pixels))
|
|
|
|
|
|
2024-01-20 06:39:49 +02:00
|
|
|
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
|
|
|
|
|
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)
|
|
|
|
|
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR)
|
|
|
|
|
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
|
2024-01-19 23:55:07 +02:00
|
|
|
|
2024-01-20 06:39:49 +02:00
|
|
|
zephr_ctx.font.atlas_tex_id = texture
|
2024-01-19 23:55:07 +02:00
|
|
|
|
2024-01-20 06:39:49 +02:00
|
|
|
gl.BindTexture(gl.TEXTURE_2D, 0)
|
2024-01-19 23:55:07 +02:00
|
|
|
|
2024-01-20 06:39:49 +02:00
|
|
|
FT.Done_Face(face)
|
|
|
|
|
FT.Done_FreeType(ft)
|
2024-01-19 23:55:07 +02:00
|
|
|
|
2024-01-20 06:39:49 +02:00
|
|
|
return 0
|
2024-01-19 23:55:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
init_fonts :: proc(font_path: cstring) -> i32 {
|
2024-01-21 03:42:06 +02:00
|
|
|
context.logger = logger
|
2024-01-20 06:39:49 +02:00
|
|
|
font_vbo, font_ebo: u32
|
2024-01-19 23:55:07 +02:00
|
|
|
|
2024-01-20 06:39:49 +02:00
|
|
|
res := init_freetype(font_path)
|
2024-01-19 23:55:07 +02:00
|
|
|
if (res != 0) {
|
2024-01-20 06:39:49 +02:00
|
|
|
return res
|
2024-01-19 23:55:07 +02:00
|
|
|
}
|
|
|
|
|
|
2024-01-20 06:39:49 +02:00
|
|
|
l_font_shader, success := create_shader("shaders/font.vert", "shaders/font.frag")
|
2024-01-19 23:55:07 +02:00
|
|
|
|
|
|
|
|
if (!success) {
|
2024-01-21 03:42:06 +02:00
|
|
|
log.fatal("Failed to create font shader")
|
2024-01-20 06:39:49 +02:00
|
|
|
return -2
|
2024-01-19 23:55:07 +02:00
|
|
|
}
|
|
|
|
|
|
2024-01-20 06:39:49 +02:00
|
|
|
font_shader = l_font_shader
|
2024-01-19 23:55:07 +02:00
|
|
|
|
2024-01-20 06:39:49 +02:00
|
|
|
gl.GenVertexArrays(1, &font_vao)
|
|
|
|
|
gl.GenBuffers(1, &font_vbo)
|
|
|
|
|
gl.GenBuffers(1, &font_instance_vbo)
|
|
|
|
|
gl.GenBuffers(1, &font_ebo)
|
2024-01-19 23:55:07 +02:00
|
|
|
|
|
|
|
|
quad_vertices := [4][2]f32 {
|
|
|
|
|
{0.0, 1.0}, // top left
|
|
|
|
|
{1.0, 1.0}, // top right
|
|
|
|
|
{1.0, 0.0}, // bottom right
|
|
|
|
|
{0.0, 0.0}, // bottom left
|
2024-01-20 06:39:49 +02:00
|
|
|
}
|
2024-01-19 23:55:07 +02:00
|
|
|
|
|
|
|
|
quad_indices := [6]u32 {
|
|
|
|
|
0, 1, 2,
|
|
|
|
|
2, 3, 0,
|
2024-01-20 06:39:49 +02:00
|
|
|
}
|
2024-01-19 23:55:07 +02:00
|
|
|
|
2024-01-20 06:39:49 +02:00
|
|
|
gl.BindVertexArray(font_vao)
|
2024-01-19 23:55:07 +02:00
|
|
|
|
|
|
|
|
// font quad vbo
|
2024-01-20 06:39:49 +02:00
|
|
|
gl.BindBuffer(gl.ARRAY_BUFFER, font_vbo)
|
|
|
|
|
gl.BufferData(gl.ARRAY_BUFFER, size_of(quad_vertices), &quad_vertices, gl.STATIC_DRAW)
|
|
|
|
|
gl.EnableVertexAttribArray(0)
|
|
|
|
|
gl.VertexAttribPointer(0, 2, gl.FLOAT, gl.FALSE, 2 * size_of(f32), 0)
|
2024-01-19 23:55:07 +02:00
|
|
|
|
|
|
|
|
// font instance vbo
|
2024-01-20 06:39:49 +02:00
|
|
|
gl.BindBuffer(gl.ARRAY_BUFFER, font_instance_vbo)
|
|
|
|
|
gl.EnableVertexAttribArray(1)
|
|
|
|
|
gl.VertexAttribPointer(1, 4, gl.FLOAT, gl.FALSE, size_of(GlyphInstance), 0)
|
|
|
|
|
gl.VertexAttribDivisor(1, 1)
|
|
|
|
|
gl.EnableVertexAttribArray(2)
|
|
|
|
|
gl.VertexAttribIPointer(2, 1, gl.INT, size_of(GlyphInstance), size_of(Vec4))
|
|
|
|
|
gl.VertexAttribDivisor(2, 1)
|
|
|
|
|
gl.EnableVertexAttribArray(3)
|
|
|
|
|
gl.VertexAttribPointer(3, 4, gl.FLOAT, gl.FALSE, size_of(GlyphInstance), (size_of(Vec4) + size_of(i32)))
|
|
|
|
|
gl.VertexAttribDivisor(3, 1)
|
|
|
|
|
gl.EnableVertexAttribArray(4)
|
|
|
|
|
gl.EnableVertexAttribArray(5)
|
|
|
|
|
gl.EnableVertexAttribArray(6)
|
|
|
|
|
gl.EnableVertexAttribArray(7)
|
|
|
|
|
gl.VertexAttribPointer(4, 4, gl.FLOAT, gl.FALSE, size_of(GlyphInstance), (size_of(Vec4) * 2 + size_of(i32)))
|
|
|
|
|
gl.VertexAttribPointer(5, 4, gl.FLOAT, gl.FALSE, size_of(GlyphInstance), (size_of(Vec4) * 3 + size_of(i32)))
|
|
|
|
|
gl.VertexAttribPointer(6, 4, gl.FLOAT, gl.FALSE, size_of(GlyphInstance), (size_of(Vec4) * 4 + size_of(i32)))
|
|
|
|
|
gl.VertexAttribPointer(7, 4, gl.FLOAT, gl.FALSE, size_of(GlyphInstance), (size_of(Vec4) * 5 + size_of(i32)))
|
|
|
|
|
gl.VertexAttribDivisor(4, 1)
|
|
|
|
|
gl.VertexAttribDivisor(5, 1)
|
|
|
|
|
gl.VertexAttribDivisor(6, 1)
|
|
|
|
|
gl.VertexAttribDivisor(7, 1)
|
2024-01-19 23:55:07 +02:00
|
|
|
|
|
|
|
|
// font ebo
|
2024-01-20 06:39:49 +02:00
|
|
|
gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, font_ebo)
|
|
|
|
|
gl.BufferData(gl.ELEMENT_ARRAY_BUFFER, size_of(quad_indices), &quad_indices, gl.STATIC_DRAW)
|
2024-01-19 23:55:07 +02:00
|
|
|
|
2024-01-20 06:39:49 +02:00
|
|
|
use_shader(font_shader)
|
2024-01-19 23:55:07 +02:00
|
|
|
|
|
|
|
|
for i in 32..<128 {
|
|
|
|
|
for j in 0..<4 {
|
|
|
|
|
buf: [24]byte
|
2024-01-20 06:39:49 +02:00
|
|
|
text := fmt.bprintf(buf[:], "texcoords[%d]", (i - 32) * 4 + j)
|
|
|
|
|
set_vec2f(font_shader, cstring(raw_data(text)), zephr_ctx.font.characters[i].tex_coords[j].x, zephr_ctx.font.characters[i].tex_coords[j].y)
|
2024-01-19 23:55:07 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-20 06:39:49 +02:00
|
|
|
gl.BindBuffer(gl.ARRAY_BUFFER, 0)
|
|
|
|
|
gl.BindVertexArray(0)
|
2024-01-19 23:55:07 +02:00
|
|
|
|
2024-01-20 06:39:49 +02:00
|
|
|
return 0
|
2024-01-19 23:55:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
calculate_text_size :: proc(text: string, font_size: u32) -> Vec2 {
|
2024-01-20 06:39:49 +02:00
|
|
|
scale := cast(f32)font_size / FONT_PIXEL_SIZE
|
2024-01-19 23:55:07 +02:00
|
|
|
size : Vec2 = ---
|
2024-01-20 06:39:49 +02:00
|
|
|
w: f32 = 0
|
|
|
|
|
h: f32 = 0
|
|
|
|
|
max_bearing_h: f32 = 0
|
2024-01-19 23:55:07 +02:00
|
|
|
|
|
|
|
|
// NOTE: I don't like looping through the characters twice, but it's fine for now
|
|
|
|
|
for i in 0..<len(text) {
|
2024-01-20 06:39:49 +02:00
|
|
|
ch := zephr_ctx.font.characters[text[i]]
|
|
|
|
|
max_bearing_h = max(max_bearing_h, ch.bearing.y)
|
2024-01-19 23:55:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for i in 0..<len(text) {
|
2024-01-20 06:39:49 +02:00
|
|
|
ch := zephr_ctx.font.characters[text[i]]
|
|
|
|
|
w += cast(f32)ch.advance / 64
|
2024-01-19 23:55:07 +02:00
|
|
|
|
|
|
|
|
// remove bearing of first character
|
|
|
|
|
if (i == 0 && len(text) > 1) {
|
2024-01-20 06:39:49 +02:00
|
|
|
w -= ch.bearing.x
|
2024-01-19 23:55:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// remove the trailing width of the last character
|
|
|
|
|
if (i == len(text) - 1) {
|
2024-01-20 06:39:49 +02:00
|
|
|
w -= ((cast(f32)ch.advance / 64) - (ch.bearing.x + ch.size.x))
|
2024-01-19 23:55:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// if we only have one character in the text, then remove the bearing width
|
|
|
|
|
if (len(text) == 1) {
|
2024-01-20 06:39:49 +02:00
|
|
|
w -= (ch.bearing.x)
|
2024-01-19 23:55:07 +02:00
|
|
|
}
|
|
|
|
|
|
2024-01-20 06:39:49 +02:00
|
|
|
h = max(h, max_bearing_h - ch.bearing.y + ch.size.y)
|
2024-01-19 23:55:07 +02:00
|
|
|
|
|
|
|
|
if (text[i] == '\n') {
|
2024-01-20 06:39:49 +02:00
|
|
|
w = 0
|
|
|
|
|
h += max_bearing_h
|
2024-01-19 23:55:07 +02:00
|
|
|
}
|
|
|
|
|
}
|
2024-01-20 06:39:49 +02:00
|
|
|
size.x = w * scale
|
|
|
|
|
size.y = h * scale
|
2024-01-19 23:55:07 +02:00
|
|
|
|
2024-01-20 06:39:49 +02:00
|
|
|
return size
|
2024-01-19 23:55:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
draw_text :: proc(text: string, font_size: u32, constraints: UiConstraints, color: Color, alignment: Alignment) {
|
2024-01-20 06:39:49 +02:00
|
|
|
glyph_instance_list := get_glyph_instance_list_from_text(text, font_size, constraints, color, alignment)
|
2024-01-19 23:55:07 +02:00
|
|
|
|
2024-01-20 06:39:49 +02:00
|
|
|
gl.ActiveTexture(gl.TEXTURE0)
|
|
|
|
|
gl.BindTexture(gl.TEXTURE_2D, zephr_ctx.font.atlas_tex_id)
|
|
|
|
|
gl.BindVertexArray(font_vao)
|
2024-01-19 23:55:07 +02:00
|
|
|
|
2024-01-20 06:39:49 +02:00
|
|
|
gl.BindBuffer(gl.ARRAY_BUFFER, font_instance_vbo)
|
|
|
|
|
gl.BufferData(gl.ARRAY_BUFFER, size_of(GlyphInstance) * len(glyph_instance_list), raw_data(glyph_instance_list), gl.DYNAMIC_DRAW)
|
2024-01-19 23:55:07 +02:00
|
|
|
|
|
|
|
|
gl.DrawElementsInstanced(gl.TRIANGLES, 6, gl.UNSIGNED_INT, nil, cast(i32)len(glyph_instance_list))
|
|
|
|
|
|
2024-01-20 06:39:49 +02:00
|
|
|
gl.BindVertexArray(0)
|
|
|
|
|
gl.BindTexture(gl.TEXTURE_2D, 0)
|
2024-01-19 23:55:07 +02:00
|
|
|
|
|
|
|
|
delete(glyph_instance_list)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
draw_text_batch :: proc(batch: ^GlyphInstanceList) {
|
2024-01-20 06:39:49 +02:00
|
|
|
gl.ActiveTexture(gl.TEXTURE0)
|
|
|
|
|
gl.BindTexture(gl.TEXTURE_2D, zephr_ctx.font.atlas_tex_id)
|
|
|
|
|
gl.BindVertexArray(font_vao)
|
2024-01-19 23:55:07 +02:00
|
|
|
|
2024-01-20 06:39:49 +02:00
|
|
|
gl.BindBuffer(gl.ARRAY_BUFFER, font_instance_vbo)
|
|
|
|
|
gl.BufferData(gl.ARRAY_BUFFER, size_of(GlyphInstance) * len(batch), raw_data(batch^), gl.DYNAMIC_DRAW)
|
2024-01-19 23:55:07 +02:00
|
|
|
|
|
|
|
|
gl.DrawElementsInstanced(gl.TRIANGLES, 6, gl.UNSIGNED_INT, nil, cast(i32)len(batch))
|
|
|
|
|
|
2024-01-20 06:39:49 +02:00
|
|
|
gl.BindVertexArray(0)
|
|
|
|
|
gl.BindTexture(gl.TEXTURE_2D, 0)
|
2024-01-19 23:55:07 +02:00
|
|
|
|
|
|
|
|
delete(batch^)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
get_glyph_instance_list_from_text :: proc(text: string, font_size: u32, constraints: UiConstraints, color: Color, alignment: Alignment) -> GlyphInstanceList {
|
|
|
|
|
constraints := constraints
|
2024-01-20 06:39:49 +02:00
|
|
|
use_shader(font_shader)
|
|
|
|
|
text_color := Vec4{ cast(f32)color.r / 255, cast(f32)color.g / 255, cast(f32)color.b / 255, cast(f32)color.a / 255 }
|
2024-01-19 23:55:07 +02:00
|
|
|
|
|
|
|
|
set_mat4f(font_shader, "projection", zephr_ctx.projection)
|
|
|
|
|
|
|
|
|
|
rect: Rect
|
|
|
|
|
|
2024-01-20 06:39:49 +02:00
|
|
|
apply_constraints(&constraints, &rect.pos, &rect.size)
|
2024-01-19 23:55:07 +02:00
|
|
|
|
2024-01-20 06:39:49 +02:00
|
|
|
text_size := calculate_text_size(text, FONT_PIXEL_SIZE)
|
|
|
|
|
font_scale := cast(f32)font_size / FONT_PIXEL_SIZE * rect.size.x
|
2024-01-19 23:55:07 +02:00
|
|
|
|
2024-01-20 06:39:49 +02:00
|
|
|
apply_alignment(alignment, &constraints, Vec2{ text_size.x * font_scale, text_size.y * font_scale }, &rect.pos)
|
2024-01-19 23:55:07 +02:00
|
|
|
|
|
|
|
|
model := identity()
|
2024-01-20 06:39:49 +02:00
|
|
|
scale(&model, Vec3{font_scale, font_scale, 1})
|
2024-01-19 23:55:07 +02:00
|
|
|
|
|
|
|
|
// rotate around the center point of the text
|
2024-01-20 06:39:49 +02:00
|
|
|
translate(&model, Vec3{-text_size.x * font_scale / 2, -text_size.y * font_scale / 2, 0})
|
|
|
|
|
rotate(&model, constraints.rotation, Vec3{0, 0, 1})
|
|
|
|
|
translate(&model, Vec3{text_size.x *font_scale / 2, text_size.y * font_scale / 2, 0})
|
2024-01-19 23:55:07 +02:00
|
|
|
|
2024-01-20 06:39:49 +02:00
|
|
|
translate(&model, Vec3{rect.pos.x, rect.pos.y, 0})
|
2024-01-19 23:55:07 +02:00
|
|
|
|
2024-01-20 06:39:49 +02:00
|
|
|
max_bearing_h: f32 = 0
|
2024-01-19 23:55:07 +02:00
|
|
|
for i in 0..<len(text) {
|
2024-01-20 06:39:49 +02:00
|
|
|
ch := zephr_ctx.font.characters[text[i]]
|
|
|
|
|
max_bearing_h = max(max_bearing_h, ch.bearing.y)
|
2024-01-19 23:55:07 +02:00
|
|
|
}
|
|
|
|
|
|
2024-01-20 06:39:49 +02:00
|
|
|
first_char_bearing_w := zephr_ctx.font.characters[text[0]].bearing.x
|
2024-01-19 23:55:07 +02:00
|
|
|
|
|
|
|
|
glyph_instance_list: GlyphInstanceList
|
|
|
|
|
reserve(&glyph_instance_list, 16)
|
|
|
|
|
|
|
|
|
|
// we use the original text and character sizes in the loop and then we just
|
|
|
|
|
// scale up or down the model matrix to get the desired font size.
|
|
|
|
|
// this way everything works out fine and we get to transform the text using the
|
|
|
|
|
// model matrix
|
2024-01-20 06:39:49 +02:00
|
|
|
c := 0
|
|
|
|
|
x: u32 = 0
|
|
|
|
|
y: f32 = 0
|
2024-01-19 23:55:07 +02:00
|
|
|
for c != len(text) {
|
|
|
|
|
ch := zephr_ctx.font.characters[text[c]]
|
|
|
|
|
// subtract the bearing width of the first character to remove the extra space
|
|
|
|
|
// at the start of the text and move every char to the left by that width
|
2024-01-20 06:39:49 +02:00
|
|
|
xpos := (cast(f32)x + (ch.bearing.x - first_char_bearing_w))
|
|
|
|
|
ypos := y + (text_size.y - ch.bearing.y - (text_size.y - max_bearing_h))
|
2024-01-19 23:55:07 +02:00
|
|
|
|
|
|
|
|
if (text[c] == '\n') {
|
2024-01-20 06:39:49 +02:00
|
|
|
x = 0
|
|
|
|
|
y += max_bearing_h + (36 * LINE_HEIGHT)
|
|
|
|
|
c += 1
|
|
|
|
|
continue
|
2024-01-19 23:55:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
test_model := Mat4 {
|
|
|
|
|
1, 2, 3, 4,
|
|
|
|
|
5, 6, 7, 8,
|
|
|
|
|
9, 10, 11, 12,
|
|
|
|
|
13, 14, 15, 16,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
instance := GlyphInstance {
|
|
|
|
|
pos = Vec4{xpos, ypos, ch.size.x, ch.size.y},
|
|
|
|
|
tex_coords_idx = cast(u32)text[c] - 32,
|
|
|
|
|
color = text_color,
|
|
|
|
|
model = model,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
append(&glyph_instance_list, instance)
|
|
|
|
|
|
2024-01-20 06:39:49 +02:00
|
|
|
x += (ch.advance >> 6)
|
|
|
|
|
c += 1
|
2024-01-19 23:55:07 +02:00
|
|
|
}
|
|
|
|
|
|
2024-01-20 06:39:49 +02:00
|
|
|
return glyph_instance_list
|
2024-01-19 23:55:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
add_text_instance :: proc(batch: ^GlyphInstanceList, text: string, font_size: u32, constraints: UiConstraints, color: Color, alignment: Alignment) {
|
2024-01-20 06:39:49 +02:00
|
|
|
glyph_instance_list := get_glyph_instance_list_from_text(text, font_size, constraints, color, alignment)
|
2024-01-19 23:55:07 +02:00
|
|
|
|
|
|
|
|
append(batch, ..glyph_instance_list[:])
|
|
|
|
|
|
2024-01-20 06:39:49 +02:00
|
|
|
delete(glyph_instance_list)
|
2024-01-19 23:55:07 +02:00
|
|
|
}
|
|
|
|
|
|