#include "context.hpp" #include "assets/cache.hpp" gui::Context::Context(gfx::DrawList& dlist, std::shared_ptr default_font) : dlist_(dlist), font_(std::move(default_font)), va_(gfx::VA_POSITION | gfx::VA_UV | gfx::VA_COLOR, gfx::VF_CREATE_EBO | gfx::VF_DYNAMIC) { white_tex_ = assets::CacheManager::GetTexture("data/white.png"); } void gui::Context::Begin(const glm::vec2& viewport_size) { vertices_.clear(); indices_.clear(); ranges_.clear(); viewport_size_ = viewport_size; } void gui::Context::DrawRect(const glm::vec2& p0, const glm::vec2& p1, uint32_t color) { BeginTexture(white_tex_.get()); PushRect(p0, glm::vec2(0.0f), p1, glm::vec2(1.0f), color); } static uint32_t DecodeUTF8Codepoint(const char*& p, const char* end) { if (p == end) return 0; if ((*p & 0b10000000) == 0) { // 1-byte sequence return *p++; } uint32_t codepoint = 0; if ((*p & 0b11100000) == 0b11000000) { // 2-byte seq codepoint = (*p++ & 0b00011111) << 6; if (p == end) return 0; codepoint |= (*p++ & 0b00111111); } else if ((*p & 0b11110000) == 0b11100000) { // 3-byte seq codepoint = (*p++ & 0b00001111) << 12; if (p == end) return 0; codepoint |= (*p++ & 0b00111111) << 6; if (p == end) return 0; codepoint |= (*p++ & 0b00111111); } else if ((*p & 0b11111000) == 0b11110000) { // 4-byte seq codepoint = (*p++ & 0b00000111) << 18; if (p == end) return 0; codepoint |= (*p++ & 0b00111111) << 12; if (p == end) return 0; codepoint |= (*p++ & 0b00111111) << 6; if (p == end) return 0; codepoint |= (*p++ & 0b00111111); } return codepoint; } glm::vec2 gui::Context::MeasureText(std::string_view text) { glm::vec2 size(0.0f); const char* p = text.data(); const char* end = p + text.size(); if (p == end) // empty string return size; uint32_t cp = 0; const float line_height = font_->GetLineHeight(); float space_size = glm::floor(line_height * 0.3f); glm::vec2 cursor(0.0f); while (cp = DecodeUTF8Codepoint(p, end)) { if (cp == ' ') { cursor.x += space_size; // Move cursor for space continue; } else if (cp == '\n') { cursor.x = 0.0f; cursor.y += line_height; continue; } else if (cp == '^') { if (!(cp = DecodeUTF8Codepoint(p, end))) break; if (cp == 'r') continue; // parse color for (size_t i = 0; i < 3; ++i) { if (!(cp = DecodeUTF8Codepoint(p, end))) break; } } const FontGlyphData* glyph = font_->GetCodepointGlyph(cp); if (!glyph) continue; // Dont even have "missing" glyph, font is shit cursor.x += glyph->advance; size.x = glm::max(size.x, cursor.x); } size.y = cursor.y + line_height; return size; } void gui::Context::DrawText(std::string_view text, const glm::vec2& pos, uint32_t color, float scale) { const char* p = text.data(); const char* end = p + text.size(); if (p == end) // empty string return; BeginTexture(font_->GetTexture().get()); uint32_t cp = 0; const float line_height = font_->GetLineHeight() * scale; float space_size = glm::floor(line_height * 0.3f); glm::vec2 cursor = pos; uint32_t curr_color = color; while (cp = DecodeUTF8Codepoint(p, end)) { if (cp == ' ') { cursor.x += space_size; // Move cursor for space continue; } else if (cp == '\n') { cursor.x = 0.0f; cursor.y += line_height; continue; } else if (cp == '^') { if (!(cp = DecodeUTF8Codepoint(p, end))) break; if (cp == 'r') // reset color { curr_color = color; continue; } curr_color = 0; // parse color for (size_t i = 0; i < 3; ++i) { curr_color >>= 8; uint32_t ch; if (cp >= '0' && cp <= '9') ch = cp - '0'; else if (cp >= 'a' && cp <= 'f') ch = cp - 'a' + 10; else ch = 0; curr_color |= (ch << 16); curr_color |= (ch << 20); if (!(cp = DecodeUTF8Codepoint(p, end))) break; } curr_color |= (color & 0xFF000000); // preserve alpha } const FontGlyphData* glyph = font_->GetCodepointGlyph(cp); if (!glyph) continue; // Dont even have "missing" glyph, font is shit glm::vec2 p0 = cursor + glyph->offset * scale; glm::vec2 p1 = p0 + glyph->size * scale; PushRect(p0, glyph->uv0, p1, glyph->uv1, curr_color); cursor.x += glyph->advance * scale; } } void gui::Context::DrawTextAligned(std::string_view text, const glm::vec2& pos, const glm::vec2& align, uint32_t color, float scale) { auto size = MeasureText(text) * scale; DrawText(text, pos + size * align, color, scale); } void gui::Context::Render() { va_.SetVBOData(vertices_.data(), vertices_.size() * sizeof(vertices_[0])); va_.SetIndices(indices_.data(), indices_.size()); for (const auto& range : ranges_) { gfx::DrawHudCmd hudcmd; hudcmd.va = &va_; hudcmd.texture = range.texture; hudcmd.first = range.start; hudcmd.count = range.count; dlist_.AddHUD(hudcmd); } } void gui::Context::BeginTexture(const gfx::Texture* texture) { if (!ranges_.empty() && ranges_.back().texture == texture) return; auto& range = ranges_.emplace_back(); range.start = indices_.size() / 3; range.count = 0; range.texture = texture; } void gui::Context::PushRect(const glm::vec2& p0, const glm::vec2& uv0, const glm::vec2& p1, const glm::vec2& uv1, uint32_t color) { uint32_t base_index = vertices_.size(); vertices_.emplace_back(glm::vec3(p0.x, p0.y, 0.0f), color, uv0); vertices_.emplace_back(glm::vec3(p1.x, p0.y, 0.0f), color, glm::vec2(uv1.x, uv0.y)); vertices_.emplace_back(glm::vec3(p1.x, p1.y, 0.0f), color, uv1); vertices_.emplace_back(glm::vec3(p0.x, p1.y, 0.0f), color, glm::vec2(uv0.x, uv1.y)); indices_.push_back(base_index + 0); indices_.push_back(base_index + 1); indices_.push_back(base_index + 2); indices_.push_back(base_index + 0); indices_.push_back(base_index + 2); indices_.push_back(base_index + 3); ranges_.back().count += 2; // 2 tris }