diff --git a/src/AssetManager.c b/src/AssetManager.c index c4aac3d..52cbc17 100644 --- a/src/AssetManager.c +++ b/src/AssetManager.c @@ -455,6 +455,10 @@ const i32 asset_loadTTFont(AssetManager *assetManager, MemoryArena *arena, font->tex = &assetManager->textures[texlist_font]; font->atlas = &assetManager->texAtlas[texlist_font]; + // NOTE(doyle): Formula derived from STB Font + font->verticalSpacing = + font->metrics.ascent - font->metrics.descent + font->metrics.lineGap; + for (i32 i = 0; i < numGlyphs; i++) { i32 glyphBitmapSizeInBytes = CAST(i32) glyphBitmaps[i].dimensions.w * @@ -489,3 +493,25 @@ void asset_addAnimation(AssetManager *assetManager, MemoryArena *arena, assetManager->anims[animId] = anim; } + +v2 asset_stringDimInPixels(const Font *const font, const char *const string) +{ + v2 stringDim = V2(0, 0); + for (i32 i = 0; i < common_strlen(string); i++) + { + i32 codepoint = string[i]; + +#ifdef DENGINE_DEBUG + ASSERT(codepoint >= font->codepointRange.x && + codepoint <= font->codepointRange.y) +#endif + + i32 relativeIndex = CAST(i32)(codepoint - font->codepointRange.x); + + v2 charDim = font->charMetrics[relativeIndex].trueSize; + stringDim.x += charDim.x; + stringDim.y = (charDim.y > stringDim.y) ? charDim.y : stringDim.y; + } + + return stringDim; +} diff --git a/src/Debug.c b/src/Debug.c index e0b41ec..54a54ef 100644 --- a/src/Debug.c +++ b/src/Debug.c @@ -81,7 +81,7 @@ void debug_init(MemoryArena *arena, v2 windowSize, Font font) { GLOBAL_debug.font = font; GLOBAL_debug.callCount = PLATFORM_MEM_ALLOC(arena, debugcallcount_num, i32); - GLOBAL_debug.stringLineGap = 1.1f * asset_getVFontSpacing(font.metrics); + GLOBAL_debug.stringLineGap = CAST(f32)font.verticalSpacing; /* Init debug string stack */ GLOBAL_debug.numDebugStrings = 0; @@ -357,7 +357,7 @@ void debug_drawUi(GameState *state, f32 dt) renderer_string(&state->renderer, &state->arena, camera, font, debugString, strPos, V2(0, 0), 0, color); - f32 stringLineGap = 1.1f * asset_getVFontSpacing(font->metrics); + f32 stringLineGap = 1.1f * font->verticalSpacing; strPos.y -= GLOBAL_debug.stringLineGap; char entityPosStr[128]; diff --git a/src/Renderer.c b/src/Renderer.c index e4edefc..d8e088e 100644 --- a/src/Renderer.c +++ b/src/Renderer.c @@ -211,7 +211,9 @@ void renderer_string(Renderer *const renderer, MemoryArena *arena, Rect camera, v2 posInCameraSpace = v2_sub(pos, camera.pos); pos = posInCameraSpace; - f32 baseline = pos.y; + + // TODO(doyle): Find why font is 1px off, might be arial font semantics + f32 baseline = pos.y - font->verticalSpacing + 1; for (i32 i = 0; i < strLen; i++) { // NOTE(doyle): Atlas packs fonts tightly, so offset the codepoint diff --git a/src/UserInterface.c b/src/UserInterface.c index 94aa87d..f66d6c8 100644 --- a/src/UserInterface.c +++ b/src/UserInterface.c @@ -1,11 +1,16 @@ #include "Dengine/UserInterface.h" +#include "Dengine/AssetManager.h" #include "Dengine/Assets.h" #include "Dengine/Renderer.h" +#include "Dengine/Debug.h" i32 userInterface_button(UiState *const uiState, + MemoryArena *const arena, AssetManager *const assetManager, - Renderer *const renderer, const KeyInput input, - const i32 id, const Rect rect) + Renderer *const renderer, + Font *const font, + const KeyInput input, + const i32 id, const Rect rect, const char *const label) { if (math_pointInRect(rect, input.mouseP)) { @@ -33,25 +38,60 @@ i32 userInterface_button(UiState *const uiState, renderer_staticRect(renderer, v2_add(V2(8, 8), rect.pos), rect.size, V2(0, 0), 0, renderTex, V4(0, 0, 0, 1)); + v2 buttonOffset = V2(0, 0); if (uiState->hotItem == id) { if (uiState->activeItem == id) { - renderer_staticRect(renderer, v2_add(V2(2, 2), rect.pos), rect.size, - V2(0, 0), 0, renderTex, V4(1, 1, 1, 1)); + buttonOffset = V2(2, 2); + renderer_staticRect(renderer, v2_add(buttonOffset, rect.pos), + rect.size, V2(0, 0), 0, renderTex, + V4(1, 1, 1, 1)); } else { - renderer_staticRect(renderer, v2_add(V2(0, 0), rect.pos), rect.size, - V2(0, 0), 0, renderTex, V4(1, 1, 1, 1)); + renderer_staticRect(renderer, v2_add(buttonOffset, rect.pos), + rect.size, V2(0, 0), 0, renderTex, + V4(1, 1, 1, 1)); } } else { - renderer_staticRect(renderer, v2_add(V2(0, 0), rect.pos), rect.size, + renderer_staticRect(renderer, v2_add(buttonOffset, rect.pos), rect.size, V2(0, 0), 0, renderTex, V4(0.5f, 0.5f, 0.5f, 1)); } + if (label) + { + v2 labelDim = asset_stringDimInPixels(font, label); + DEBUG_PUSH_VAR("label dim: %4.2f, %4.2f", labelDim, "v2"); + v2 labelPos = rect.pos; + if (labelDim.w < rect.size.w) + { + // Initially position the label to half the width of the button + labelPos.x += (rect.size.w * 0.5f); + + // Move the label pos back half the length of the string (i.e. + // center it) + labelPos.x -= (CAST(f32)labelDim.w * 0.5f); + } + + if (labelDim.h < rect.size.h) + { + labelPos.y += (rect.size.h * 0.5f); + labelPos.y -= (CAST(f32)labelDim.h * 0.5f); + } + + labelPos = v2_add(labelPos, buttonOffset); + renderer_staticString(renderer, arena, font, label, labelPos, V2(0, 0), + 0, V4(0, 0, 0, 1)); + + v2 rulerPos = rect.pos; + rulerPos.y -= 10; + renderer_staticRect(renderer, rulerPos, V2(10, 10), V2(0, 0), 0, + renderTex, V4(0.5f, 0.1f, 0.7f, 1)); + } + // After renderering before click check, see if we need to process keys if (uiState->kbdItem == id) { @@ -194,7 +234,7 @@ i32 userInterface_scrollBar(UiState *const uiState, return 0; } -i32 userInterface_textField(UiState *const uiState, MemoryArena *arena, +i32 userInterface_textField(UiState *const uiState, MemoryArena *const arena, AssetManager *const assetManager, Renderer *const renderer, Font *const font, KeyInput input, const i32 id, v2 pos, @@ -244,8 +284,7 @@ i32 userInterface_textField(UiState *const uiState, MemoryArena *arena, renderTex, V4(0.5f, 0.5f, 0.5f, 1)); } - // TODO(doyle): Figure out why we need to offset text .. - v2 strPos = v2_add(textRect.pos, V2(0, -font->maxSize.h)); + v2 strPos = textRect.pos; renderer_staticString(renderer, arena, font, string, strPos, V2(0, 0), 0, V4(0, 0, 0, 1)); diff --git a/src/WorldTraveller.c b/src/WorldTraveller.c index a8a837a..98d9e09 100644 --- a/src/WorldTraveller.c +++ b/src/WorldTraveller.c @@ -1305,16 +1305,16 @@ void worldTraveller_gameUpdateAndRender(GameState *state, f32 dt) /* Draw ui */ Rect buttonRectA = {V2(300, 500), V2(100, 50)}; - userInterface_button(&state->uiState, assetManager, renderer, state->input, - 1, buttonRectA); + userInterface_button(&state->uiState, &state->arena, assetManager, renderer, + font, state->input, 1, buttonRectA, "HELLO WORLD"); Rect buttonRectB = {V2(500, 500), V2(100, 50)}; - userInterface_button(&state->uiState, assetManager, renderer, state->input, - 2, buttonRectB); + userInterface_button(&state->uiState, &state->arena, assetManager, renderer, + font, state->input, 2, buttonRectB, "button2"); Rect buttonRectC = {V2(700, 500), V2(100, 50)}; - userInterface_button(&state->uiState, assetManager, renderer, state->input, - 3, buttonRectC); + userInterface_button(&state->uiState, &state->arena, assetManager, renderer, + font, state->input, 3, buttonRectC, "button3"); LOCAL_PERSIST i32 scrollValue = 30; Rect scrollRectA = {V2(900, 500), V2(16, 255)}; @@ -1356,6 +1356,9 @@ void worldTraveller_gameUpdateAndRender(GameState *state, f32 dt) renderer_staticString(&state->renderer, &state->arena, font, heroAvatarStr, strPos, V2(0, 0), 0, V4(0, 0, 1, 1)); + renderer_staticString(&state->renderer, &state->arena, font, heroAvatarStr, + strPos, V2(0, 0), 0, V4(0, 0, 1, 1)); + for (i32 i = 0; i < world->maxEntities; i++) { Entity *entity = &world->entities[i]; diff --git a/src/include/Dengine/AssetManager.h b/src/include/Dengine/AssetManager.h index b8c96d5..a2131de 100644 --- a/src/include/Dengine/AssetManager.h +++ b/src/include/Dengine/AssetManager.h @@ -43,14 +43,10 @@ const i32 asset_loadShaderFiles(AssetManager *assetManager, MemoryArena *arena, const i32 asset_loadTTFont(AssetManager *assetManager, MemoryArena *arena, const char *filePath); -inline i32 asset_getVFontSpacing(FontMetrics metrics) -{ - i32 result = metrics.ascent - metrics.descent + metrics.lineGap; - return result; -} - void asset_addAnimation(AssetManager *assetManager, MemoryArena *arena, i32 texId, i32 animId, i32 *atlasIndexes, i32 numFrames, f32 frameDuration); +v2 asset_stringDimInPixels(const Font *const font, const char *const string); + #endif diff --git a/src/include/Dengine/Assets.h b/src/include/Dengine/Assets.h index 24ea2c7..ab9fad6 100644 --- a/src/include/Dengine/Assets.h +++ b/src/include/Dengine/Assets.h @@ -118,8 +118,13 @@ typedef struct Font Texture *tex; FontMetrics metrics; + + // NOTE(doyle): Array of character's by ASCII value starting from + // codepointRange and their metrics CharMetrics *charMetrics; + i32 verticalSpacing; + v2 codepointRange; v2 maxSize; diff --git a/src/include/Dengine/UserInterface.h b/src/include/Dengine/UserInterface.h index 3544cff..f80541b 100644 --- a/src/include/Dengine/UserInterface.h +++ b/src/include/Dengine/UserInterface.h @@ -43,11 +43,16 @@ typedef struct UiState } UiState; i32 userInterface_button(UiState *const uiState, + MemoryArena *const arena, AssetManager *const assetManager, - Renderer *const renderer, const KeyInput input, - const i32 id, const Rect rect); + Renderer *const renderer, + Font *const font, + const KeyInput input, + const i32 id, const Rect rect, + const char *const label); -i32 userInterface_textField(UiState *const uiState, MemoryArena *arena, +i32 userInterface_textField(UiState *const uiState, + MemoryArena *const arena, AssetManager *const assetManager, Renderer *const renderer, Font *const font, KeyInput input, const i32 id, v2 pos, diff --git a/src/include/Dengine/WorldTraveller.h b/src/include/Dengine/WorldTraveller.h index a86e22e..ef0de9b 100644 --- a/src/include/Dengine/WorldTraveller.h +++ b/src/include/Dengine/WorldTraveller.h @@ -16,6 +16,11 @@ /* Forward declaration */ typedef struct Entity Entity; +typedef struct Config +{ + b32 playWorldAudio; +} Config; + typedef struct World { Entity *entities; @@ -49,6 +54,7 @@ typedef struct GameState AssetManager assetManager; AudioManager audioManager; + Config config; MemoryArena arena; UiState uiState; } GameState;