From eb3c81cd04dbfb68dc1e3e3ac4862e3d746203eb Mon Sep 17 00:00:00 2001 From: James Bulman Date: Sun, 5 Oct 2025 21:11:18 +0100 Subject: [PATCH] Added unproject Broken and buggy font stuff Fixed some typos Fixed draw rect not using dim properly --- code/core/impl/math.c | 12 ++- code/core/math.h | 9 ++ code/draw/core.c | 191 ++++++++++++++++++++++++++++++++- code/draw/core.h | 43 +++++++- code/first.c | 29 ++++- code/game/core.c | 46 ++++++-- code/game/core.h | 6 ++ code/game/impl/world.c | 21 ++++ code/game/world.h | 2 + code/vulkan/core.c | 2 + code/vulkan/shaders/basic.vert | 10 +- 11 files changed, 349 insertions(+), 22 deletions(-) diff --git a/code/core/impl/math.c b/code/core/impl/math.c index 8859ff0..9490038 100644 --- a/code/core/impl/math.c +++ b/code/core/impl/math.c @@ -18,6 +18,11 @@ R2f R2F(V2f min, V2f max) { return result; } +V2f V2f_Scale(V2f x, F32 s) { + V2f result = { x.x * s, x.y * s }; + return result; +} + V3f V3f_Neg(V3f x) { V3f result = { -x.x, -x.y, -x.z }; return result; @@ -28,6 +33,11 @@ V3f V3f_Scale(V3f x, F32 s) { return result; } +V3f V3f_Sub(V3f a, V3f b) { + V3f result = { a.x - b.x, a.y - b.y, a.z - b.z }; + return result; +} + F32 V3f_Dot(V3f a, V3f b) { F32 result = (a.x * b.x) + (a.y * b.y) + (a.z * b.z); return result; @@ -140,7 +150,7 @@ Mat4x4FInv M4x4F_CameraView(V3f x, V3f y, V3f z, V3f p) { // V3f ix = V3f_Scale(x, 1.0f / V3f_Dot(x, x)); V3f iy = V3f_Scale(y, 1.0f / V3f_Dot(y, y)); - V3f iz = V3f_Scale(z, 1.0f / V3f_Dot(z, y)); + V3f iz = V3f_Scale(z, 1.0f / V3f_Dot(z, z)); // Calculate inverse position // diff --git a/code/core/math.h b/code/core/math.h index 7ac915d..8f1a4e1 100644 --- a/code/core/math.h +++ b/code/core/math.h @@ -98,14 +98,23 @@ struct R2f { V2f max; }; +typedef struct R3f R3f; +struct R3f { + V3f min; + V3f max; +}; + function V2f V2F(F32 x, F32 y); function V3f V3F(F32 x, F32 y, F32 z); function V4f V4F(F32 x, F32 y, F32 z, F32 w); function R2f R2F(V2f min, V2f max); +function V2f V2f_Scale(V2f x, F32 s); + function V3f V3f_Neg(V3f x); function V3f V3f_Scale(V3f x, F32 s); +function V3f V3f_Sub(V3f a, V3f b); function F32 V3f_Dot(V3f a, V3f b); function F32 V4f_Dot(V4f a, V4f b); diff --git a/code/draw/core.c b/code/draw/core.c index 8d1c763..0f70005 100644 --- a/code/draw/core.c +++ b/code/draw/core.c @@ -30,7 +30,8 @@ void D_End(D_Context *draw, Vk_Frame *frame) { rbo_info.offset = 0; rbo_info.range = VK_WHOLE_SIZE; - VkDescriptorImageInfo *image_info = M_ArenaPush(temp.arena, VkDescriptorImageInfo, .count = draw->n_images); + U32 total = draw->n_images + draw->n_fonts; + VkDescriptorImageInfo *image_info = M_ArenaPush(temp.arena, VkDescriptorImageInfo, .count = total); for (U32 it = 0; it < draw->n_images; ++it) { image_info[it].imageView = draw->images[it].image.view; @@ -38,6 +39,15 @@ void D_End(D_Context *draw, Vk_Frame *frame) { image_info[it].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; } + U32 idx = draw->n_images; + for (D_Font *it = draw->fonts; it != 0; it = it->next) { + image_info[idx].imageView = it->image.view; + image_info[idx].sampler = vk.sampler; // @Todo: probably want linear filtering on fonts + image_info[idx].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + + idx += 1; + } + writes[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; writes[0].dstSet = set; writes[0].dstBinding = 0; @@ -48,7 +58,7 @@ void D_End(D_Context *draw, Vk_Frame *frame) { writes[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; writes[1].dstSet = set; writes[1].dstBinding = 1; - writes[1].descriptorCount = draw->n_images; + writes[1].descriptorCount = total; writes[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; writes[1].pImageInfo = image_info; @@ -111,8 +121,8 @@ void _D_Rect(D_Context *draw, D_RectOpts *opts) { } if (opts->flags & D_RECT_IGNORE_ASPECT) { - rect->w = opts->w; - rect->h = opts->h; + rect->w = opts->dim.w; + rect->h = opts->dim.h; } else { Vk_Image *image = &draw->images[opts->texture].image; @@ -130,3 +140,176 @@ void _D_Rect(D_Context *draw, D_RectOpts *opts) { draw->n_rects += 1; } } + +void D_Text(D_Context *draw, D_Font *font, Str8 text, F32 x, F32 y) { + F32 xoff = x; + local_persist B32 done = 0; + + for (S64 it = 0; it < text.count; ++it) { + if (text.data[it] >= ' ' && text.data[it] <= '~') { + D_Glyph *glyph = &font->glyphs[text.data[it] - ' ']; + + V2f size; + size.w = (glyph->box.max.x - glyph->box.min.x); + size.h = (glyph->box.max.y - glyph->box.min.y); + + V2f dim; + + F32 scale = 150.0f / (font->ascent - font->descent); + + if (size.w > size.h) { + dim.w = scale * (size.w / size.h); + dim.h = scale; + } + else { + dim.w = scale; + dim.h = scale * (size.h / size.w); + } + + // @Hardcode: need font index in font struct + D_Rect(draw, xoff, y, .texture = 0, .c = V4F(0, 1, 1, 1), .dim = dim, .flags = D_RECT_IGNORE_ASPECT); + D_Rect(draw, xoff, y, .texture = draw->n_images, .uv = glyph->box, .dim = dim, .flags = D_RECT_IGNORE_ASPECT); + xoff += (1.05f * dim.w); // glyph->advance; //+ glyph->offset.x; + + if (!done) { printf(" %f (%f, %f)", xoff, dim.w, dim.h); } + } + } + + if (!done) { printf("\n"); } + done = true; +} + +void D_FontLoad(D_Context *draw, Str8 name, F32 size) { + M_TempScope(0, 0) { + (void) draw; + + Str8 exe_path = FS_SystemPath(temp.arena, FS_SYSTEM_PATH_EXE); + Str8 path = Sf(temp.arena, "%.*s/assets/%.*s.ttf", Sv(exe_path), Sv(name)); + + Str8 font_data = FS_ReadEntireFile(temp.arena, path); + + stbtt_fontinfo info = { 0 }; + stbtt_InitFont(&info, font_data.data, 0); + + F32 scale = stbtt_ScaleForPixelHeight(&info, 20); + + U32 w = 512; + U32 h = 512; + + U8 *pixels = M_ArenaPush(temp.arena, U8, .count = (w * h)); + + stbtt_pack_context pack = { 0 }; + stbtt_PackBegin(&pack, pixels, w, h, 0, 1, 0); + + U32 count = '~' - ' '; + stbtt_packedchar *packed = M_ArenaPush(temp.arena, stbtt_packedchar, .count = count); + stbtt_PackFontRange(&pack, font_data.data, 0, size, ' ', count, packed); + + D_Font *font = M_ArenaPush(draw->arena, D_Font); + + font->px = size; + + S32 asc, desc, line; + stbtt_GetFontVMetrics(&info, &asc, &desc, &line); + + font->line_advance = cast(F32) line; + font->ascent = cast(F32) asc; + font->descent = cast(F32) desc; + + font->glyphs = M_ArenaPush(draw->arena, D_Glyph, .count = count); + + for (U32 it = 0; it < count; ++it) { + D_Glyph *glyph = &font->glyphs[it]; + stbtt_packedchar *chr = &packed[it]; + + S32 left, width; + stbtt_GetCodepointHMetrics(&info, ' ' + it, &width, &left); + + glyph->box = R2F(V2F(chr->x0 / (F32) w, chr->y0 / (F32) h), V2F(chr->x1 / (F32) w, chr->y1 / (F32) h)); + + glyph->advance = glyph->box.max.x - glyph->box.min.x; //chr->xadvance * scale; + glyph->offset = V2F(chr->xoff * scale, chr->yoff * scale); + } + + Vk_Buffer *staging = &draw->staging; + U32 *px = (U32 *) staging->data; + for (U32 y = 0; y < h; ++y) { + for (U32 x = 0; x < w; ++x) { + *px++ = 0x0 | ((U32) pixels[(y * w) + x] << 24); + } + } + + Vk_CommandBuffer *cmds = Vk_CommandBufferPush(); + + font->image.width = w; + font->image.height = h; + + font->image.format = VK_FORMAT_R8G8B8A8_SRGB; + font->image.usage = VK_IMAGE_USAGE_SAMPLED_BIT; + + Vk_ImageCreate(&font->image); + + VkBufferImageCopy copy = { 0 }; + + copy.bufferOffset = 0; + copy.bufferRowLength = 0; + copy.bufferImageHeight = 0; + + copy.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + copy.imageSubresource.mipLevel = 0; + copy.imageSubresource.baseArrayLayer = 0; + copy.imageSubresource.layerCount = 1; + + copy.imageExtent.width = w; + copy.imageExtent.height = h; + copy.imageExtent.depth = 1; + + VkImageMemoryBarrier2 transfer = { 0 }; + VkImageMemoryBarrier2 shader_read = { 0 }; + + transfer.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2; + transfer.srcStageMask = VK_PIPELINE_STAGE_2_NONE; + transfer.srcAccessMask = VK_ACCESS_2_NONE; + transfer.dstStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT; + transfer.dstAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT; + transfer.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; + transfer.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + transfer.image = font->image.handle; + + transfer.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + transfer.subresourceRange.layerCount = 1; + transfer.subresourceRange.levelCount = 1; + + shader_read.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2; + shader_read.srcStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT; + shader_read.srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT; + shader_read.dstStageMask = VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT; + shader_read.dstAccessMask = VK_ACCESS_2_SHADER_SAMPLED_READ_BIT; + shader_read.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + shader_read.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + shader_read.image = font->image.handle; + + shader_read.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + shader_read.subresourceRange.layerCount = 1; + shader_read.subresourceRange.levelCount = 1; + + VkDependencyInfo dep = { 0 }; + dep.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO; + + dep.imageMemoryBarrierCount = 1; + dep.pImageMemoryBarriers = &transfer; + + vk.CmdPipelineBarrier2(cmds->handle, &dep); + + vk.CmdCopyBufferToImage(cmds->handle, staging->handle, font->image.handle, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©); + + dep.pImageMemoryBarriers = &shader_read; + + vk.CmdPipelineBarrier2(cmds->handle, &dep); + + Vk_CommandBufferSubmit(cmds, true); + + SLL_PushN(draw->fonts, font, next); + draw->n_fonts += 1; + } +} diff --git a/code/draw/core.h b/code/draw/core.h index f4e24c8..a5f7b5b 100644 --- a/code/draw/core.h +++ b/code/draw/core.h @@ -3,6 +3,29 @@ #define D_MAX_RECTS (262144) +typedef struct D_Glyph D_Glyph; +struct D_Glyph { + F32 advance; + V2f offset; + R2f box; +}; + +typedef struct D_Font D_Font; +struct D_Font { + D_Font *next; + + F32 px; + + F32 line_advance; + F32 ascent; + F32 descent; + + F32 *kerning; + D_Glyph *glyphs; + + Vk_Image image; +}; + typedef struct D_Image D_Image; struct D_Image { Str8 name; @@ -30,6 +53,9 @@ struct G_Camera; typedef struct D_Context D_Context; struct D_Context { Vk_Buffer *rbo; + Vk_Buffer staging; + + M_Arena *arena; U32 n_pipelines; Vk_Pipeline *pipelines; @@ -37,6 +63,9 @@ struct D_Context { U32 n_images; D_Image *images; + U32 n_fonts; + D_Font *fonts; + U32 max_rects; U32 n_rects; D_Rect *rects; @@ -65,9 +94,15 @@ struct D_RectOpts { F32 angle; union { - F32 w, h; - F32 scale, _h; V2f dim; + + struct { + F32 w, h; + }; + + struct { + F32 scale, _h; + }; }; union { @@ -79,7 +114,11 @@ struct D_RectOpts { function void D_Begin(D_Context *draw, Vk_Frame *frame, U32 max_rects); function void D_End(D_Context *draw, Vk_Frame *frame); +function void D_Text(D_Context *draw, D_Font *font, Str8 text, F32 x, F32 y); + function void _D_Rect(D_Context *draw, D_RectOpts *opts); #define D_Rect(draw, x, y, ...) _D_Rect(draw, &(D_RectOpts) { .p = V2F(x, y), .uv = R2F(V2F(0, 0), V2F(1, 1)), .scale = 1, .c = V4F(1, 1, 1, 1), ##__VA_ARGS__ }) +function void D_FontLoad(D_Context *draw, Str8 path, F32 size); + #endif // LD_DRAW_CORE_H_ diff --git a/code/first.c b/code/first.c index 3e14cf5..c52cee8 100644 --- a/code/first.c +++ b/code/first.c @@ -5,6 +5,12 @@ #define STB_IMAGE_IMPLEMENTATION 1 #include +#define STB_RECT_PACK_IMPLEMENTATION 1 +#include + +#define STB_TRUETYPE_IMPLEMENTATION 1 +#include + #include "core/core.h" #include "core/types.h" #include "game/npc.h" @@ -44,6 +50,7 @@ int main(int argc, char **argv) { game = M_ArenaPush(arena, G_State); game->arena = arena; + game->draw.arena = arena; G_ImagesLoad(game); G_PipelinesLoad(game); @@ -99,6 +106,24 @@ int main(int argc, char **argv) { { running = false; } + else if (e.type == SDL_EVENT_KEY_DOWN) { + if (e.key.key == SDLK_W) { + game->camera.p.y -= 0.2f; + } + + if (e.key.key == SDLK_S) { + game->camera.p.y += 0.2f; + } + + if (e.key.key == SDLK_A) { + game->camera.p.x -= 0.2f; + } + + if (e.key.key == SDLK_D) { + game->camera.p.x += 0.2f; + } + } + ProcessEvents(&e, &world); } @@ -140,10 +165,6 @@ int main(int argc, char **argv) { D_Begin(&game->draw, frame, D_MAX_RECTS); - D_Rect(&game->draw, 0.0f, 0.0f, .texture = 1); - D_Rect(&game->draw, -8.0f, 0.0f, .texture = 2, .scale = 2.0f); - D_Rect(&game->draw, 6.0f, 0.0f, .texture = 3); - D_End(&game->draw, frame); vk.CmdEndRendering(cmd); diff --git a/code/game/core.c b/code/game/core.c index 9fbc249..609555e 100644 --- a/code/game/core.c +++ b/code/game/core.c @@ -9,14 +9,14 @@ void G_ImagesLoad(G_State *game) { FS_List assets = FS_PathList(temp.arena, path); - Vk_Buffer staging = { 0 }; - staging.size = MB(256); - staging.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; - staging.host_visible = true; + Vk_Buffer *staging = &draw->staging; + staging->size = MB(256); + staging->usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; + staging->host_visible = true; - Vk_BufferCreate(&staging); + Vk_BufferCreate(staging); - U8 *base = staging.data; + U8 *base = staging->data; U64 offset = 0; Vk_CommandBuffer *cmds = Vk_CommandBufferPush(); @@ -108,7 +108,7 @@ void G_ImagesLoad(G_State *game) { vk.CmdPipelineBarrier2(cmds->handle, &dep); - vk.CmdCopyBufferToImage(cmds->handle, staging.handle, white->image.handle, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©); + vk.CmdCopyBufferToImage(cmds->handle, staging->handle, white->image.handle, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©); dep.pImageMemoryBarriers = &shader_read; @@ -145,7 +145,7 @@ void G_ImagesLoad(G_State *game) { base += image_sz; offset += image_sz; - Assert(offset <= staging.size); + Assert(offset <= staging->size); draw->n_images += 1; @@ -198,7 +198,7 @@ void G_ImagesLoad(G_State *game) { vk.CmdPipelineBarrier2(cmds->handle, &dep); - vk.CmdCopyBufferToImage(cmds->handle, staging.handle, image->image.handle, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©); + vk.CmdCopyBufferToImage(cmds->handle, staging->handle, image->image.handle, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©); dep.pImageMemoryBarriers = &shader_read; @@ -251,7 +251,7 @@ void G_PipelinesLoad(G_State *game) { bindings[1].binding = 1; bindings[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - bindings[1].descriptorCount = game->draw.n_images; + bindings[1].descriptorCount = game->draw.n_images + game->draw.n_fonts; bindings[1].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; VkDescriptorSetLayoutCreateInfo set_info = { 0 }; @@ -298,6 +298,32 @@ void G_CalculateCamera(G_Camera *camera, F32 aspect) { camera->proj.inv = M4x4F_Mul(view.inv, proj.inv); } +V3f G_CameraUnprojectAt(G_Camera *camera, V2f clip, F32 z) { + V3f dir = V3f_Sub(camera->p, V3f_Scale(camera->z, z)); + V4f z_dist = V4F(dir.x, dir.y, dir.z, 1.0f); + V4f persp = M4x4F_VMul4(camera->proj.fwd, z_dist); + + clip = V2f_Scale(clip, persp.w); + + V4f world = M4x4F_VMul4(camera->proj.inv, V4F(clip.x, clip.y, persp.z, persp.w)); + + V3f result = world.xyz; + return result; +} + +V3f G_CameraUnproject(G_Camera *camera, V2f clip) { + V3f result = G_CameraUnprojectAt(camera, clip, camera->p.z); + return result; +} + +R3f G_CameraBounds(G_Camera *camera) { + R3f result; + result.min = G_CameraUnproject(camera, V2F(-1, -1)); + result.max = G_CameraUnproject(camera, V2F( 1, 1)); + + return result; +} + #include "impl/aabb.c" #include "impl/nav.c" #include "impl/player.c" diff --git a/code/game/core.h b/code/game/core.h index 01a4498..ef6797d 100644 --- a/code/game/core.h +++ b/code/game/core.h @@ -25,6 +25,12 @@ function void G_PipelinesLoad(G_State *game); function void G_CalculateCamera(G_Camera *camera, F32 aspect); +// Assumes 'calculate' has been called +function V3f G_CameraUnprojectAt(G_Camera *camera, V2f clip, F32 z); +function V3f G_CameraUnproject(G_Camera *camera, V2f clip); + +function R3f G_CameraBounds(G_Camera *camera); + #include "aabb.h" #include "player.h" #include "nav.h" diff --git a/code/game/impl/world.c b/code/game/impl/world.c index 405601f..3f218d7 100644 --- a/code/game/impl/world.c +++ b/code/game/impl/world.c @@ -16,3 +16,24 @@ void UpdateNPCs(F32 delta, World *world) { void ProcessEvents(SDL_Event *event, World *world) { PlayerUpdate(event, &world->player); } + +void G_WorldDraw(G_State *game, World *world) { + D_Context *draw = &game->draw; + + (void) world; + + for (F32 y = -128; y < 128; y += 1.1f) { + for (F32 x = -128; x < 128; x += 1.1f) { + + U32 ux = (U32) x; + U32 uy = (U32) y; + + U32 tid = 15; + if ((ux % 11) == 0 || ((uy % 7) == 0)) { + tid = 16; + } + + D_Rect(draw, x, y, .texture = tid); + } + } +} diff --git a/code/game/world.h b/code/game/world.h index 433070b..8bc969c 100644 --- a/code/game/world.h +++ b/code/game/world.h @@ -37,4 +37,6 @@ function void ProcessEvents(SDL_Event *event, World *world); function void UpdateNPCs(F32 delta, World *world); function void updateNPC(F32 delta, NPC *npc, World *world); +function void G_WorldDraw(G_State *game, World *world); + #endif // LD_GAME_WORLD_H_ diff --git a/code/vulkan/core.c b/code/vulkan/core.c index 1c1b9af..d463853 100644 --- a/code/vulkan/core.c +++ b/code/vulkan/core.c @@ -518,6 +518,8 @@ Vk_CommandBuffer *Vk_CommandBufferPush() { vk.BeginCommandBuffer(result->handle, &begin_info); + frame->next_scratch += 1; + return result; } diff --git a/code/vulkan/shaders/basic.vert b/code/vulkan/shaders/basic.vert index 9277166..5bed5c5 100644 --- a/code/vulkan/shaders/basic.vert +++ b/code/vulkan/shaders/basic.vert @@ -50,11 +50,19 @@ vec4 unorm_colour(uint c) { return result; } +vec2 rotate2f(vec2 v, float angle) { + float s = sin(angle); + float c = cos(angle); + + vec2 result = vec2(c * v.x - s * v.y, s * v.x + c * v.y); + return result; +} + void main() { G_Rect rect = rects[gl_InstanceIndex]; uint idx = indices[gl_VertexIndex]; - vec2 p = (verticies[idx] * vec2(rect.w, rect.h)) + vec2(rect.x, rect.y); + vec2 p = (rotate2f(verticies[idx], rect.angle) * vec2(rect.w, rect.h)) + vec2(rect.x, rect.y); gl_Position = proj * vec4(p, 1.0f, 1.0f);