first version of font atlas generator complete

This commit is contained in:
matthew 2025-08-14 12:20:43 +10:00
parent 2780f811f0
commit 63c942294c
6 changed files with 68 additions and 31 deletions

Binary file not shown.

View File

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

View File

@ -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)
{

View File

@ -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 = {

View File

@ -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;

View File

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