From 63c942294cabd7158ba8c357b37ce81500e1d2b5 Mon Sep 17 00:00:00 2001 From: matthew Date: Thu, 14 Aug 2025 12:20:43 +1000 Subject: [PATCH] first version of font atlas generator complete --- assets/shaders/gui.vert.spv | Bin 4148 -> 4148 bytes src/gears/game.d | 26 +++++++++--------- src/gears/main.d | 14 +++++----- src/gears/vulkan.d | 2 +- src/shaders/gui.vert.glsl | 6 ++--- src/shared/font.d | 51 +++++++++++++++++++++++++++++++----- 6 files changed, 68 insertions(+), 31 deletions(-) diff --git a/assets/shaders/gui.vert.spv b/assets/shaders/gui.vert.spv index 2884b57fa89563d7d121acf7bff36a3c190ad9e1..fb83d4f6edd2aa14980a533d4dc35d148d77b867 100644 GIT binary patch delta 36 ocmdm@utj0R0iMZMcm*b3;PGK}nk>s34d!+50(qOS^2Rd(00RCD@Bjb+ delta 36 ocmdm@utj0R0iMZMcm*b3;PGK}nJmj24d!+50(qOS^2Rd(00S}%^Z)<= diff --git a/src/gears/game.d b/src/gears/game.d index 9a89048..2d27077 100644 --- a/src/gears/game.d +++ b/src/gears/game.d @@ -10,6 +10,7 @@ import math; import core.stdc.math : cosf, sinf; import std.algorithm.sorting; import fonts; +import main; f32 g_DELTA; @@ -109,13 +110,7 @@ InitGame(PlatformWindow* window) u8[16*16*4] white_tex; white_tex[] = u8.max; - u8[] atlas = LoadAssetData(&g.frame_arena, "textures/atlas.png"); - int width, height, has_ch; - auto img = stbi_load_from_memory(atlas.ptr, cast(int)atlas.length, &width, &height, &has_ch, 4); - assert(width == FONT_ATLAS.width && height == FONT_ATLAS.height && has_ch == 1, "atlas height and width do not match"); - u8[] img_slice = img[0 .. width * height * 4]; - - CreateImageView(&g.rd, &g.font_tex, FONT_ATLAS.width, FONT_ATLAS.height, 4, img_slice); + CreateImageView(&g.rd, &g.font_tex, FONT_ATLAS_TEST.atlas.width, FONT_ATLAS_TEST.atlas.height, 4, FONT_ATLAS_TEST.data); // TODO: fix buffer overflow between two textures //CreateImageView(&g.rd, &g.default_tex, 16, 16, 4, white_tex); @@ -177,7 +172,7 @@ Cycle(Game* g) Reset(&g.frame_arena); DrawRect(g, 500.0, 500.0, 800.0, 800.0, Vec4(0.2, 0.3, 0.7, 1.0)); - DrawText(g, 200.0, 200.0, FONT_ATLAS.size, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"); + DrawText(g, 200.0, 200.0, 128.0, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"); BeginFrame(&g.rd); @@ -323,19 +318,20 @@ void DrawText(Game* g, f32 x, f32 y, f32 px, string str) { f32 x_pos = x; + f32 scale = px / FONT_ATLAS_TEST.atlas.size; foreach(ch; str) { - foreach(glyph; FONT_ATLAS.glyphs) + foreach(glyph; FONT_ATLAS_TEST.atlas.glyphs) { if (ch == glyph.ch) { UIVertex* v = g.ui_vertex_buf.ptr + g.ui_count; - f32 r = px * glyph.plane_right; - f32 l = px * glyph.plane_left; + f32 r = glyph.plane_right * scale; + f32 l = glyph.plane_left * scale; f32 w = r - l; - f32 h = px * (glyph.plane_bottom - glyph.plane_top); - f32 y_pos = px * glyph.plane_bottom; + f32 h = (glyph.plane_bottom - glyph.plane_top) * scale; + f32 y_pos = glyph.plane_top * scale; v.dst_start.x = x_pos + l; v.dst_start.y = y + h - y_pos; @@ -349,9 +345,11 @@ DrawText(Game* g, f32 x, f32 y, f32 px, string str) v.col = Vec4(1.0, 1.0, 1.0, 1.0); - x_pos += px * glyph.advance; + x_pos += glyph.advance * scale; AddUIIndices(g); + + break; } } } diff --git a/src/gears/main.d b/src/gears/main.d index d00d4f8..98d34da 100644 --- a/src/gears/main.d +++ b/src/gears/main.d @@ -1,4 +1,4 @@ -public import includes; +import includes; import std.stdio; import aliases; import core.memory; @@ -13,6 +13,8 @@ import font; import alloc; import assets; +FontAtlasBuf FONT_ATLAS_TEST; + // TODO: // 1. Determine how to better handle inputs // 2. Set up multisampling @@ -20,14 +22,14 @@ import assets; void main(string[] argv) { - PlatformWindow window = CreateWindow("Video Game", 1920, 1080); - Game g = InitGame(&window); - - Arena arena = CreateArena(MB(8)); + Arena arena = CreateArena(MB(32)); InitFreeType(); u8[] font_data = LoadAssetData(&arena, "fonts/NuberNextCondensed-DemiBold.otf"); FontFace font = OpenFont(font_data); - FontAtlasBuf atlas = CreateAtlas(&arena, font, 32.0, 256); + FONT_ATLAS_TEST = CreateAtlas(&arena, font, 128.0, 2048); + + PlatformWindow window = CreateWindow("Video Game", 1920, 1080); + Game g = InitGame(&window); while (true) { diff --git a/src/gears/vulkan.d b/src/gears/vulkan.d index 4552310..569c8bc 100644 --- a/src/gears/vulkan.d +++ b/src/gears/vulkan.d @@ -1685,7 +1685,7 @@ CreateGraphicsPipeline(Vulkan* vk, GfxPipelineInfo* build_info) cullMode: VK_CULL_MODE_BACK_BIT, polygonMode: VK_POLYGON_MODE_FILL, lineWidth: 1.0, - frontFace: VK_FRONT_FACE_CLOCKWISE, + frontFace: VK_FRONT_FACE_COUNTER_CLOCKWISE, }; VkPipelineMultisampleStateCreateInfo multisample_info = { diff --git a/src/shaders/gui.vert.glsl b/src/shaders/gui.vert.glsl index 75d4a61..e8ce4e8 100644 --- a/src/shaders/gui.vert.glsl +++ b/src/shaders/gui.vert.glsl @@ -41,10 +41,10 @@ void main() vec2 src_pos = (Vertices[gl_VertexIndex] * src_half_size + src_center); vec2 uvs[4] = vec2[4]( - vec2(in_src_start.x, in_src_end.y), vec2(in_src_start.x, in_src_start.y), - vec2(in_src_end.x, in_src_end.y), - vec2(in_src_end.x, in_src_start.y) + vec2(in_src_start.x, in_src_end.y), + vec2(in_src_end.x, in_src_start.y), + vec2(in_src_end.x, in_src_end.y) ); FragData.color = in_col; diff --git a/src/shared/font.d b/src/shared/font.d index 237e0ab..52cb650 100644 --- a/src/shared/font.d +++ b/src/shared/font.d @@ -18,6 +18,15 @@ InitFreeType() FT_Init_FreeType(&FT_LIB); } +void +CloseFreeType() +{ + if (FT_LIB) + { + FT_Done_FreeType(FT_LIB); + } +} + FontFace OpenFont(u8[] data) { @@ -36,12 +45,17 @@ CloseFont(FontFace font) } FontAtlasBuf -CreateAtlas(Arena* arena, FontFace font, f32 size, u64 dimension) +CreateAtlas(Arena* arena, FontFace font, f32 size, u32 dimension) { assert(dimension >= 128, "Dimension must be at least 128"); FontAtlasBuf atlas = { data: AllocArray!(u8)(arena, dimension * dimension * 4), + atlas: { + size: size, + width: dimension, + height: dimension, + }, }; // TODO: proper packing algorithm @@ -88,11 +102,15 @@ CreateAtlas(Arena* arena, FontFace font, f32 size, u64 dimension) current_h = 0; count = 0; + u32 font_w = font.size.metrics.x_ppem; + u32 font_h = font.size.metrics.y_ppem; + char_code = FT_Get_First_Char(font, &index); while (index != 0) { FT_Load_Char(font, char_code, cast(FT_Int32)FT_LOAD_RENDER); + FT_GlyphSlot glyph = font.glyph; FT_Bitmap* bmp = &font.glyph.bitmap; i32 top = font.glyph.bitmap_top; i32 left = font.glyph.bitmap_left; @@ -103,29 +121,48 @@ CreateAtlas(Arena* arena, FontFace font, f32 size, u64 dimension) max_w = 0; } + i32 x, y; foreach(r; 0 .. bmp.rows) { - i32 y = cast(i32)(max_h + r); + y = cast(i32)(max_h + r); foreach(c; 0 .. bmp.width) { - i32 x = max_w + left + c; + x = max_w + c; u64 offset = (y*dimension + x) * 4; - atlas.data[offset+0] = 255; - atlas.data[offset+1] = 255; - atlas.data[offset+2] = 255; - atlas.data[offset+3] = bmp.buffer[r*bmp.pitch + c]; + atlas.data[offset+0] = bmp.buffer[r*bmp.pitch + c]; + atlas.data[offset+1] = bmp.buffer[r*bmp.pitch + c]; + atlas.data[offset+2] = bmp.buffer[r*bmp.pitch + c]; + atlas.data[offset+3] = 255; } } + Glyph* g = atlas.atlas.glyphs.ptr + count; + + g.ch = cast(dchar)char_code; + g.advance = cast(f32)(glyph.advance.x >> 6); + g.plane_left = cast(f32)left; + g.plane_right = g.plane_left + bmp.width; + g.plane_top = cast(f32)top; + g.plane_bottom = g.plane_top + bmp.rows; + + g.atlas_top = max_h; + g.atlas_left = max_w; + g.atlas_bottom = max_h + bmp.rows; + g.atlas_right = max_w + bmp.width; + max_w += bmp.width; current_h = bmp.rows > current_h ? bmp.rows : current_h; char_code = FT_Get_Next_Char(font, char_code, &index); + + count += 1; } } + + return atlas; }