diff --git a/assets/opensb/font/mono-b.ttf b/assets/opensb/font/mono-b.ttf new file mode 100644 index 0000000..20e3449 Binary files /dev/null and b/assets/opensb/font/mono-b.ttf differ diff --git a/assets/opensb/font/mono-bi.ttf b/assets/opensb/font/mono-bi.ttf new file mode 100644 index 0000000..ff2ea5a Binary files /dev/null and b/assets/opensb/font/mono-bi.ttf differ diff --git a/assets/opensb/font/mono-i.ttf b/assets/opensb/font/mono-i.ttf new file mode 100644 index 0000000..f36282f Binary files /dev/null and b/assets/opensb/font/mono-i.ttf differ diff --git a/assets/opensb/font/mono.ttf b/assets/opensb/font/mono.ttf new file mode 100644 index 0000000..04e56b9 Binary files /dev/null and b/assets/opensb/font/mono.ttf differ diff --git a/assets/opensb/interface.config.patch b/assets/opensb/interface.config.patch index 020fdba..dd764bb 100644 --- a/assets/opensb/interface.config.patch +++ b/assets/opensb/interface.config.patch @@ -13,7 +13,10 @@ "cursorTooltip" : { "font" : "" }, - "debugFont" : "", + + "debugFont" : "mono-b", + "debugFontSize" : 6, + "debugFontDirectives" : "?border=1;0007;0000", // Change planet name to support the new internal string formatting. "planetNameFormatString" : "- {} -", @@ -24,5 +27,5 @@ "buttonHoverOffSound" : [ "/sfx/interface/button/hover_off.wav" ], "debugSpatialClearTime" : 0.0, - "debugOffset" : [64, 100] + "debugOffset" : [80, 80] } \ No newline at end of file diff --git a/source/client/StarClientApplication.cpp b/source/client/StarClientApplication.cpp index b4c9b1a..7a813c4 100644 --- a/source/client/StarClientApplication.cpp +++ b/source/client/StarClientApplication.cpp @@ -855,14 +855,15 @@ void ClientApplication::updateRunning() { } Vec2F aimPosition = m_player->aimPosition(); - LogMap::set("render_fps", appController()->renderFps()); - LogMap::set("update_rate", appController()->updateRate()); - LogMap::set("player_pos", strf("{:4.2f} {:4.2f}", m_player->position()[0], m_player->position()[1])); - LogMap::set("player_vel", strf("{:4.2f} {:4.2f}", m_player->velocity()[0], m_player->velocity()[1])); - LogMap::set("player_aim", strf("{:4.2f} {:4.2f}", aimPosition[0], aimPosition[1])); + float fps = appController()->renderFps(); + LogMap::set("render_rate", strf("{:4.2f} FPS ({:4.2f}ms)", fps, (1.0f / appController()->renderFps()) * 1000.0f)); + LogMap::set("update_rate", strf("{:4.2f}Hz", appController()->updateRate())); + LogMap::set("player_pos", strf("[ ^#f45;{:4.2f}^reset;, ^#49f;{:4.2f}^reset; ]", m_player->position()[0], m_player->position()[1])); + LogMap::set("player_vel", strf("[ ^#f45;{:4.2f}^reset;, ^#49f;{:4.2f}^reset; ]", m_player->velocity()[0], m_player->velocity()[1])); + LogMap::set("player_aim", strf("[ ^#f45;{:4.2f}^reset;, ^#49f;{:4.2f}^reset; ]", aimPosition[0], aimPosition[1])); if (m_universeClient->worldClient()) { LogMap::set("liquid_level", strf("{}", m_universeClient->worldClient()->liquidLevel(Vec2I::floor(aimPosition)).level)); - LogMap::set("dungeonId", strf("{}", m_universeClient->worldClient()->dungeonId(Vec2I::floor(aimPosition)))); + LogMap::set("dungeon_id", strf("{}", m_universeClient->worldClient()->dungeonId(Vec2I::floor(aimPosition)))); } if (m_mainInterface->currentState() == MainInterface::ReturnToTitle) diff --git a/source/core/StarFont.cpp b/source/core/StarFont.cpp index 6308784..9c8636a 100644 --- a/source/core/StarFont.cpp +++ b/source/core/StarFont.cpp @@ -7,6 +7,8 @@ namespace Star { +constexpr int FontLoadFlags = FT_LOAD_FORCE_AUTOHINT; + struct FTContext { FT_Library library; @@ -49,9 +51,7 @@ FontPtr Font::loadTrueTypeFont(ByteArrayConstPtr const& bytes, unsigned pixelSiz return font; } -Font::Font() { - m_pixelSize = 0; -} +Font::Font() : m_pixelSize(0) {} FontPtr Font::clone() const { return Font::loadTrueTypeFont(m_fontBuffer, m_pixelSize); @@ -78,38 +78,38 @@ unsigned Font::width(String::Char c) { if (auto width = m_widthCache.maybe({c, m_pixelSize})) { return *width; } else { - FT_Load_Char(m_fontImpl->face, c, FT_LOAD_FORCE_AUTOHINT); + FT_Load_Char(m_fontImpl->face, c, FontLoadFlags); unsigned newWidth = (m_fontImpl->face->glyph->advance.x + 32) / 64; m_widthCache.insert({c, m_pixelSize}, newWidth); return newWidth; } } -Image Font::render(String::Char c) { + +std::pair Font::render(String::Char c) { if (!m_fontImpl) throw FontException("Font::render called on uninitialized font."); - FT_UInt glyph_index = FT_Get_Char_Index(m_fontImpl->face, c); - if (FT_Load_Glyph(m_fontImpl->face, glyph_index, FT_LOAD_FORCE_AUTOHINT) != 0) + FT_Face face = m_fontImpl->face; + FT_UInt glyph_index = FT_Get_Char_Index(face, c); + if (FT_Load_Glyph(face, glyph_index, FontLoadFlags) != 0) return {}; /* convert to an anti-aliased bitmap */ - if (FT_Render_Glyph(m_fontImpl->face->glyph, FT_RENDER_MODE_NORMAL) != 0) + if (FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL) != 0) return {}; - FT_GlyphSlot slot = m_fontImpl->face->glyph; + FT_GlyphSlot slot = face->glyph; - int width = (slot->advance.x + 32) / 64; - int height = m_pixelSize; + unsigned int width = slot->bitmap.width; + unsigned int height = slot->bitmap.rows; Image image(width, height, PixelFormat::RGBA32); for (int y = 0; y < height; ++y) { + unsigned char* p = slot->bitmap.buffer + y * slot->bitmap.pitch; for (int x = 0; x < width; ++x) { - int bx = x; - int by = y + slot->bitmap_top - m_fontImpl->face->size->metrics.ascender / 64; - if (bx >= 0 && by >= 0 && bx < (int)slot->bitmap.width && by < (int)slot->bitmap.rows) { - unsigned char* p = slot->bitmap.buffer + by * slot->bitmap.pitch; - unsigned char val = *(p + bx); + if (x >= 0 && y >= 0 && x < width && y < height) { + unsigned char val = *(p + x); image.set(x, height - y - 1, Vec4B(255, 255, 255, val)); } else { image.set(x, height - y - 1, Vec4B(255, 255, 255, 0)); @@ -117,7 +117,7 @@ Image Font::render(String::Char c) { } } - return image; + return { move(image), (slot->bitmap_top - (int)height) + m_pixelSize / 4 }; } } diff --git a/source/core/StarFont.hpp b/source/core/StarFont.hpp index 954f27a..d353611 100644 --- a/source/core/StarFont.hpp +++ b/source/core/StarFont.hpp @@ -34,12 +34,13 @@ public: // May return empty image on unrenderable character (Normally, this will // render a box, but if there is an internal freetype error this may return // an empty image). - Image render(String::Char c); + std::pair render(String::Char c); private: FontImplPtr m_fontImpl; ByteArrayConstPtr m_fontBuffer; unsigned m_pixelSize; + HashMap, unsigned> m_widthCache; }; diff --git a/source/frontend/StarMainInterface.cpp b/source/frontend/StarMainInterface.cpp index 86c7bb6..37ff2e3 100644 --- a/source/frontend/StarMainInterface.cpp +++ b/source/frontend/StarMainInterface.cpp @@ -1261,33 +1261,44 @@ void MainInterface::renderDebug() { auto assets = Root::singleton().assets(); m_guiContext->setFontSize(m_config->debugFontSize); m_guiContext->setFont(m_config->debugFont); + m_guiContext->setFontProcessingDirectives(m_config->debugFontDirectives); m_guiContext->setFontColor(Color::Green.toRgba()); + m_guiContext->setFontMode(FontMode::Normal); bool clearMap = m_debugMapClearTimer.wrapTick(); auto logMapValues = LogMap::getValues(); if (clearMap) LogMap::clear(); + List formatted; + formatted.reserve(logMapValues.size()); + int counter = 0; for (auto const& pair : logMapValues) { - TextPositioning positioning = {Vec2F(m_config->debugOffset[0], windowHeight() - m_config->debugOffset[1] - m_config->fontSize * interfaceScale() * counter)}; - m_debugTextRect.combine(m_guiContext->determineTextSize(strf("{}: {}", pair.first, pair.second), positioning).padded(m_config->debugBackgroundPad)); - ++counter; + TextPositioning positioning = {Vec2F(m_config->debugOffset[0], windowHeight() - m_config->debugOffset[1] - m_config->fontSize * interfaceScale() * counter++)}; + String& text = formatted.emplace_back(strf("^white;{}^lightgray;:^reset; {}", pair.first, pair.second)); + m_debugTextRect.combine(m_guiContext->determineTextSize(text, positioning).padded(m_config->debugBackgroundPad)); } - if (!m_debugTextRect.isNull()) - m_guiContext->drawQuad(m_debugTextRect, m_config->debugBackgroundColor.toRgba()); + if (!m_debugTextRect.isNull()) { + RenderQuad& quad = m_guiContext->renderer()->immediatePrimitives() + .emplace_back(std::in_place_type_t(), m_debugTextRect, m_config->debugBackgroundColor.toRgba(), 0.0f).get(); - if (clearMap) - m_debugTextRect = RectF::null(); + quad.b.color[3] = quad.c.color[3] = 0; + }; + + m_debugTextRect = RectF::null(); counter = 0; for (auto const& pair : logMapValues) { TextPositioning positioning = {Vec2F(m_config->debugOffset[0], windowHeight() - m_config->debugOffset[1] - m_config->fontSize * interfaceScale() * counter)}; - m_guiContext->renderText(strf("{}: {}", pair.first, pair.second), positioning); + m_guiContext->renderText(formatted[counter], positioning); ++counter; } + m_guiContext->setFontSize(8); + m_guiContext->setDefaultFont(); m_guiContext->setFontColor(Vec4B::filled(255)); + m_guiContext->setFontProcessingDirectives(""); auto const& camera = m_worldPainter->camera(); diff --git a/source/frontend/StarMainInterfaceTypes.cpp b/source/frontend/StarMainInterfaceTypes.cpp index b0f7041..0731574 100644 --- a/source/frontend/StarMainInterfaceTypes.cpp +++ b/source/frontend/StarMainInterfaceTypes.cpp @@ -114,6 +114,7 @@ MainInterfaceConfigPtr MainInterfaceConfig::loadFromAssets() { config->debugOffset = jsonToVec2I(assets->json("/interface.config:debugOffset")); config->debugFontSize = assets->json("/interface.config:debugFontSize").toUInt(); config->debugFont = assets->json("/interface.config:debugFont").toString(); + config->debugFontDirectives = assets->json("/interface.config:debugFontDirectives").toString(); config->debugSpatialClearTime = assets->json("/interface.config:debugSpatialClearTime").toFloat(); config->debugMapClearTime = assets->json("/interface.config:debugMapClearTime").toFloat(); config->debugBackgroundColor = jsonToColor(assets->json("/interface.config:debugBackgroundColor")); diff --git a/source/frontend/StarMainInterfaceTypes.hpp b/source/frontend/StarMainInterfaceTypes.hpp index 4158639..e09892c 100644 --- a/source/frontend/StarMainInterfaceTypes.hpp +++ b/source/frontend/StarMainInterfaceTypes.hpp @@ -145,6 +145,7 @@ struct MainInterfaceConfig { Vec2I debugOffset; unsigned debugFontSize; String debugFont; + String debugFontDirectives; float debugSpatialClearTime; float debugMapClearTime; Color debugBackgroundColor; diff --git a/source/rendering/StarFontTextureGroup.cpp b/source/rendering/StarFontTextureGroup.cpp index 76defee..e59d15c 100644 --- a/source/rendering/StarFontTextureGroup.cpp +++ b/source/rendering/StarFontTextureGroup.cpp @@ -45,7 +45,8 @@ const FontTextureGroup::GlyphTexture& FontTextureGroup::glyphTexture(String::Cha if (res.second) { m_font->setPixelSize(size); - Image image = m_font->render(c); + auto pair = m_font->render(c); + Image& image = pair.first; if (!processingDirectives.empty()) { try { Vec2F preSize = Vec2F(image.size()); @@ -55,7 +56,7 @@ const FontTextureGroup::GlyphTexture& FontTextureGroup::glyphTexture(String::Cha border->includeTransparent = true; } image = processImageOperations(imageOperations, image); - res.first->second.processingOffset = preSize - Vec2F(image.size()); + res.first->second.offset = (preSize - Vec2F(image.size())) / 2; } catch (StarException&) { image.forEachPixel([](unsigned x, unsigned y, Vec4B& pixel) { @@ -64,8 +65,9 @@ const FontTextureGroup::GlyphTexture& FontTextureGroup::glyphTexture(String::Cha } } else - res.first->second.processingOffset = Vec2F(); + res.first->second.offset = Vec2F(); + res.first->second.offset[1] += pair.second; res.first->second.texture = m_textureGroup->create(image); } diff --git a/source/rendering/StarFontTextureGroup.hpp b/source/rendering/StarFontTextureGroup.hpp index 15d2895..6bbb120 100644 --- a/source/rendering/StarFontTextureGroup.hpp +++ b/source/rendering/StarFontTextureGroup.hpp @@ -17,7 +17,7 @@ public: struct GlyphTexture { TexturePtr texture; int64_t time; - Vec2F processingOffset; + Vec2F offset; }; FontTextureGroup(TextureGroupPtr textureGroup); diff --git a/source/rendering/StarTextPainter.cpp b/source/rendering/StarTextPainter.cpp index c779c96..1d26e53 100644 --- a/source/rendering/StarTextPainter.cpp +++ b/source/rendering/StarTextPainter.cpp @@ -586,7 +586,7 @@ void TextPainter::renderGlyph(String::Char c, Vec2F const& screenPos, unsigned f return; const FontTextureGroup::GlyphTexture& glyphTexture = m_fontTextureGroup.glyphTexture(c, fontSize, processingDirectives); - Vec2F offset = glyphTexture.processingOffset * (scale * 0.5f); + Vec2F offset = glyphTexture.offset * scale; m_renderer->immediatePrimitives().emplace_back(std::in_place_type_t(), glyphTexture.texture, screenPos + offset, scale, color, 0.0f); }