diff --git a/CMakeLists.txt b/CMakeLists.txt index 67714c0..bccce9c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -91,8 +91,6 @@ set(CLIENT_ONLY_SOURCES "src/gfx/buffer_object.cpp" "src/gfx/buffer_object.hpp" "src/gfx/draw_list.hpp" - "src/gfx/font.hpp" - "src/gfx/font.cpp" "src/gfx/frustum.hpp" "src/gfx/frustum.cpp" "src/gfx/hud.hpp" @@ -104,13 +102,15 @@ set(CLIENT_ONLY_SOURCES "src/gfx/shader.hpp" "src/gfx/shader.cpp" "src/gfx/surface.hpp" - "src/gfx/text.hpp" - "src/gfx/text.cpp" "src/gfx/texture.cpp" "src/gfx/texture.hpp" "src/gfx/uniform_buffer.hpp" "src/gfx/vertex_array.cpp" "src/gfx/vertex_array.hpp" + "src/gui/context.hpp" + "src/gui/context.cpp" + "src/gui/font.hpp" + "src/gui/font.cpp" "src/utils/files.cpp" ) diff --git a/src/assets/cache.hpp b/src/assets/cache.hpp index 452dd99..615259f 100644 --- a/src/assets/cache.hpp +++ b/src/assets/cache.hpp @@ -10,7 +10,7 @@ #ifdef CLIENT #include "audio/sound.hpp" #include "gfx/texture.hpp" -#include "gfx/font.hpp" +#include "gui/font.hpp" #endif #include @@ -61,10 +61,10 @@ protected: PtrType Load(const std::string& key) override { return audio::Sound::LoadFromFile(key); } }; -class FontCache final : public Cache +class FontCache final : public Cache { protected: - PtrType Load(const std::string& key) override { return gfx::Font::LoadFromFile(key); } + PtrType Load(const std::string& key) override { return gui::Font::LoadFromFile(key); } }; #endif // CLIENT @@ -120,7 +120,7 @@ public: return sound_cache_.Get(filename); } - static std::shared_ptr GetFont(const std::string& filename) + static std::shared_ptr GetFont(const std::string& filename) { return font_cache_.Get(filename); } diff --git a/src/client/app.cpp b/src/client/app.cpp index a66f546..fea2985 100644 --- a/src/client/app.cpp +++ b/src/client/app.cpp @@ -7,7 +7,8 @@ #include "assets/cache.hpp" #include "gameview/worldview.hpp" -App::App() +App::App() : + gui_(dlist_, assets::CacheManager::GetFont("data/comic32.font")) { std::cout << "Initializing App..." << std::endl; @@ -17,9 +18,6 @@ App::App() audiomaster_.SetMasterVolume(2.0f); #endif - font_ = assets::CacheManager::GetFont("data/comic32.font"); - - InitChat(); AddChatMessage("Test!"); } @@ -76,16 +74,19 @@ void App::Frame() params.screen_width = viewport_size_.x; params.screen_height = viewport_size_.y; + gui_.Begin(); + const game::view::WorldView* world; if (session_) { - session_->Draw(dlist_, params); + session_->Draw(dlist_, params, gui_); } // draw chat UpdateChat(); - DrawChat(dlist_); + DrawChat(); + gui_.Render(); renderer_.DrawList(dlist_, params); } @@ -144,8 +145,7 @@ void App::AddChatMessage(const std::string& text) { auto& ch = chat_.emplace_back(); ch.timeout = time_ + 10.0f; - ch.text = std::make_unique(font_, 0xFF'FF'FF'FF); - ch.text->SetText(text); + ch.text = text; UpdateChat(); } @@ -165,38 +165,21 @@ void App::SendInput(game::PlayerInputType type, bool enable) msg.Write(val); } -void App::InitChat() -{ - chatpos_.resize(20); // max messages on screen - const float chat_scale = 1.0f; - for (size_t i = 0; i < chatpos_.size(); ++i) - { - auto& hudp = chatpos_[i]; - hudp.pos.y = (i+1) * font_->GetLineHeight() * chat_scale; - hudp.scale.x = chat_scale; - hudp.scale.y = -chat_scale; - } - -} - void App::UpdateChat() { // remove expired or over the limit messages - while (!chat_.empty() && (chat_.size() > chatpos_.size() ||chat_[0].timeout < time_)) + while (!chat_.empty() && (chat_.size() > 20 ||chat_[0].timeout < time_)) { chat_.pop_front(); } } -void App::DrawChat(gfx::DrawList& dlist) +void App::DrawChat() { for (size_t i = 0; i < chat_.size(); ++i) { - const auto& va = chat_[i].text->GetVA(); + glm::vec2 pos(10.0f, static_cast(i) * gui_.GetFont()->GetLineHeight() + 10.0f); - if (va.GetNumIndices() < 1) - continue; - float t_rem = chat_[i].timeout - time_; const float fade = 1.0f; if (t_rem < fade) @@ -204,11 +187,7 @@ void App::DrawChat(gfx::DrawList& dlist) chat_[i].color.a = t_rem / fade; } - gfx::DrawHudCmd cmd; - cmd.pos = &chatpos_[i]; - cmd.texture = chat_[i].text->GetFont()->GetTexture().get(); - cmd.va = &va; - cmd.color = &chat_[i].color; - dlist.AddHUD(cmd); + uint32_t color = glm::packUnorm4x8(chat_[i].color); + gui_.DrawText(chat_[i].text, pos, color); } } diff --git a/src/client/app.hpp b/src/client/app.hpp index 7248185..4769cb1 100644 --- a/src/client/app.hpp +++ b/src/client/app.hpp @@ -5,7 +5,8 @@ #include "game/player_input.hpp" #include "gfx/renderer.hpp" -#include "gfx/text.hpp" +#include "gui/font.hpp" +#include "gui/context.hpp" #include "audio/master.hpp" #include "net/msg_producer.hpp" #include "net/inmessage.hpp" @@ -14,14 +15,13 @@ struct ChatMessage { - std::unique_ptr text; + std::string text; float timeout = 0.0f; glm::vec4 color = glm::vec4(1.0f); }; class App : public net::MsgProducer { - public: App(); @@ -49,9 +49,8 @@ public: private: void SendInput(game::PlayerInputType type, bool enable); - void InitChat(); void UpdateChat(); - void DrawChat(gfx::DrawList& dlist); + void DrawChat(); private: float time_ = 0.0f; @@ -59,19 +58,16 @@ private: game::PlayerInputFlags input_ = 0; game::PlayerInputFlags prev_input_ = 0; - float prev_time_ = 0.0f; float delta_time_ = 0.0f; gfx::Renderer renderer_; gfx::DrawList dlist_; + gui::Context gui_; audio::Master audiomaster_; std::unique_ptr session_; - std::shared_ptr font_; - std::deque chat_; - std::vector chatpos_; }; diff --git a/src/gameview/client_session.cpp b/src/gameview/client_session.cpp index 4812130..4137d98 100644 --- a/src/gameview/client_session.cpp +++ b/src/gameview/client_session.cpp @@ -73,11 +73,11 @@ void game::view::ClientSession::Update(const UpdateInfo& info) } } -void game::view::ClientSession::Draw(gfx::DrawList& dlist, gfx::DrawListParams& params) +void game::view::ClientSession::Draw(gfx::DrawList& dlist, gfx::DrawListParams& params, gui::Context& gui) { if (world_) { - DrawWorld(dlist, params); + DrawWorld(dlist, params, gui); } } @@ -148,7 +148,7 @@ bool game::view::ClientSession::ProcessChatMsg(net::InMessage& msg) return true; } -void game::view::ClientSession::DrawWorld(gfx::DrawList& dlist, gfx::DrawListParams& params) +void game::view::ClientSession::DrawWorld(gfx::DrawList& dlist, gfx::DrawListParams& params, gui::Context& gui) { // glm::mat4 view = glm::lookAt(glm::vec3(15.0f, 0.0f, 1.0f), glm::vec3(0.0f, 0.0f, -13.0f), glm::vec3(0.0f, // 0.0f, 1.0f)); @@ -164,8 +164,7 @@ void game::view::ClientSession::DrawWorld(gfx::DrawList& dlist, gfx::DrawListPar // glm::mat4 fake_view_proj = glm::perspective(glm::radians(30.0f), aspect, 0.1f, 3000.0f) * view; - game::view::DrawArgs draw_args(dlist, params.view_proj, eye, glm::ivec2(params.screen_width, params.screen_height), - 500.0f); + game::view::DrawArgs draw_args(dlist, gui, params.view_proj, eye, glm::ivec2(params.screen_width, params.screen_height), 500.0f); world_->Draw(draw_args); glm::mat4 camera_world = glm::inverse(view); diff --git a/src/gameview/client_session.hpp b/src/gameview/client_session.hpp index 4718de7..70ed182 100644 --- a/src/gameview/client_session.hpp +++ b/src/gameview/client_session.hpp @@ -25,7 +25,7 @@ public: void ProcessMouseMove(float delta_yaw, float delta_pitch); void Update(const UpdateInfo& info); - void Draw(gfx::DrawList& dlist, gfx::DrawListParams& params); + void Draw(gfx::DrawList& dlist, gfx::DrawListParams& params, gui::Context& gui); const WorldView* GetWorld() const { return world_.get(); } @@ -39,7 +39,7 @@ private: bool ProcessCameraMsg(net::InMessage& msg); bool ProcessChatMsg(net::InMessage& msg); - void DrawWorld(gfx::DrawList& dlist, gfx::DrawListParams& params); + void DrawWorld(gfx::DrawList& dlist, gfx::DrawListParams& params, gui::Context& gui); void SendViewAngles(float time); private: diff --git a/src/gameview/draw_args.hpp b/src/gameview/draw_args.hpp index 7486ae3..3cc0dc8 100644 --- a/src/gameview/draw_args.hpp +++ b/src/gameview/draw_args.hpp @@ -2,6 +2,7 @@ #include "gfx/draw_list.hpp" #include "gfx/frustum.hpp" +#include "gui/context.hpp" namespace game::view { @@ -9,6 +10,7 @@ namespace game::view struct DrawArgs { gfx::DrawList& dlist; + gui::Context& gui; const glm::mat4 view_proj; const glm::vec3 eye; @@ -16,8 +18,8 @@ struct DrawArgs const glm::ivec2 screen_size; const float render_distance; - DrawArgs(gfx::DrawList& dlist, const glm::mat4& view_proj, const glm::vec3& eye, const glm::ivec2& screen_size, float render_distance) - : dlist(dlist), view_proj(view_proj), eye(eye), frustum(view_proj), screen_size(screen_size), render_distance(render_distance) + DrawArgs(gfx::DrawList& dlist, gui::Context& gui, const glm::mat4& view_proj, const glm::vec3& eye, const glm::ivec2& screen_size, float render_distance) + : dlist(dlist), gui(gui), view_proj(view_proj), eye(eye), frustum(view_proj), screen_size(screen_size), render_distance(render_distance) { } }; diff --git a/src/gameview/entityview.cpp b/src/gameview/entityview.cpp index 1aac7b7..8d7a4d5 100644 --- a/src/gameview/entityview.cpp +++ b/src/gameview/entityview.cpp @@ -7,8 +7,7 @@ game::view::EntityView::EntityView(WorldView& world, net::InMessage& msg) : world_(world), - audioplayer_(world_.GetAudioMaster()), - nametag_text_(assets::CacheManager::GetFont("data/comic32.font"), 0xFFFFFFFF) + audioplayer_(world_.GetAudioMaster()) { // read nametag and attachment info if (!ReadNametag(msg) || !ReadAttach(msg)) @@ -80,7 +79,6 @@ bool game::view::EntityView::ReadNametag(net::InMessage& msg) return false; nametag_ = nametag; - nametag_text_.SetText(nametag); return true; } @@ -125,24 +123,14 @@ void game::view::EntityView::DrawNametag(const DrawArgs& args) if (ndc_pos.z < -1.0f || ndc_pos.z > 1.0f) return; // behind camera - nametag_pos_.anchor.x = ndc_pos.x * 0.5f + 0.5f; - nametag_pos_.anchor.y = 1.0f - (ndc_pos.y * 0.5f + 0.5f); // flip y + glm::vec2 anchor = ndc_pos * 0.5f + 0.5f; + anchor.y = 1.0f - anchor.y; + anchor *= args.screen_size; - // center float scale = 0.7f; - nametag_pos_.scale = glm::vec2(scale, -scale); - nametag_pos_.pos = nametag_text_.GetSize() * glm::vec2(-0.5f, -1.0f) * scale; - - // static const glm::vec4 nametag_color = glm::vec4(1.0f, 1.0f, 1.0f, 1.0f); - - gfx::DrawHudCmd hudcmd; - hudcmd.va = &nametag_text_.GetVA(); - hudcmd.texture = nametag_text_.GetFont()->GetTexture().get(); - hudcmd.pos = &nametag_pos_; - // hudcmd.color = &nametag_color; - args.dlist.AddHUD(hudcmd); - // std::cout << "at position: " << root_.local.position.x << ", " << root_.local.position.y << ", " << root_.local.position.z << std::endl; + glm::vec2 pos = anchor + args.gui.MeasureText(nametag_) * glm::vec2(-0.5f, -1.0f) * scale; + args.gui.DrawText(nametag_, pos, 0xFFFFFFFF, scale); } void game::view::EntityView::DrawAxes(const DrawArgs& args) diff --git a/src/gameview/entityview.hpp b/src/gameview/entityview.hpp index 0cd8cbd..f7e9c01 100644 --- a/src/gameview/entityview.hpp +++ b/src/gameview/entityview.hpp @@ -5,7 +5,6 @@ #include "audio/player.hpp" #include "draw_args.hpp" #include "game/transform_node.hpp" -#include "gfx/text.hpp" #include "net/defs.hpp" #include "net/inmessage.hpp" @@ -72,8 +71,6 @@ protected: private: std::string nametag_; - gfx::Text nametag_text_; - gfx::HudPosition nametag_pos_; net::EntNum parentnum_ = 0; float upd_time_ = 0.0f; diff --git a/src/gfx/draw_list.hpp b/src/gfx/draw_list.hpp index 0788ee9..722aa40 100644 --- a/src/gfx/draw_list.hpp +++ b/src/gfx/draw_list.hpp @@ -43,6 +43,8 @@ struct DrawHudCmd const Texture* texture = nullptr; const HudPosition* pos = nullptr; const glm::vec4* color = nullptr; + size_t first = 0; + size_t count = 0; }; struct DrawList diff --git a/src/gfx/renderer.cpp b/src/gfx/renderer.cpp index d3346b7..8df8f6b 100644 --- a/src/gfx/renderer.cpp +++ b/src/gfx/renderer.cpp @@ -150,8 +150,8 @@ void gfx::Renderer::DrawSurfaceList(std::span list, const DrawLi return a.dist > b.dist; // do not optimize blended, sort by distance instead } - if (auto cmp = sa <=> sb; cmp != 0) - return cmp < 0; + if (sa == sb) + return false; if (auto cmp = sa->texture <=> sb->texture; cmp != 0) return cmp < 0; @@ -159,6 +159,9 @@ void gfx::Renderer::DrawSurfaceList(std::span list, const DrawLi if (auto cmp = sa->va.get() <=> sb->va.get(); cmp != 0) return cmp < 0; + if (auto cmp = sa <=> sb; cmp != 0) + return cmp < 0; + return false; }); @@ -462,6 +465,9 @@ void gfx::Renderer::DrawHudList(std::span queue, const DrawListParam last_vao = cmd.va; } - glDrawElements(GL_TRIANGLES, static_cast(cmd.va->GetNumIndices()), GL_UNSIGNED_INT, NULL); + size_t first = cmd.first * 3; + size_t count = cmd.count ? cmd.count * 3 : cmd.va->GetNumIndices(); + + glDrawElements(GL_TRIANGLES, static_cast(count), GL_UNSIGNED_INT, (void*)(first * sizeof(GLuint))); } } diff --git a/src/gfx/text.cpp b/src/gfx/text.cpp deleted file mode 100644 index a94aae6..0000000 --- a/src/gfx/text.cpp +++ /dev/null @@ -1,175 +0,0 @@ -#include "text.hpp" - -#include "utils/bufferput.hpp" - -gfx::Text::Text(std::shared_ptr font, uint32_t color) - : font_(std::move(font)), va_(VA_POSITION | VA_UV | VA_COLOR, VF_CREATE_EBO | VF_DYNAMIC), color_(color) -{ -} - -static uint32_t DecodeUTF8Codepoint(const char*& p) -{ - if (!*p) - 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) - return 0; - codepoint |= (*p++ & 0b00111111); - } - else if ((*p & 0b11110000) == 0b11100000) - { // 3-byte seq - codepoint = (*p++ & 0b00001111) << 12; - if (!*p) - return 0; - codepoint |= (*p++ & 0b00111111) << 6; - if (!*p) - return 0; - codepoint |= (*p++ & 0b00111111); - } - else if ((*p & 0b11111000) == 0b11110000) - { // 4-byte seq - codepoint = (*p++ & 0b00000111) << 18; - if (!*p) - return 0; - codepoint |= (*p++ & 0b00111111) << 12; - if (!*p) - return 0; - codepoint |= (*p++ & 0b00111111) << 6; - if (!*p) - return 0; - codepoint |= (*p++ & 0b00111111); - } - - return codepoint; -} - - -struct TextVertex -{ - glm::vec3 pos; - uint32_t color; - glm::vec2 uv; -}; - -static void PutVertex(std::vector& buf, const glm::vec3 pos, const glm::vec2& uv, uint32_t color) -{ - //BufferPut(buf, pos); - //BufferPut(buf, color); - //BufferPut(buf, uv); - buf.emplace_back(TextVertex{pos, color, uv}); -} - -void gfx::Text::SetText(const char* text) -{ - static std::vector vertices; - static std::vector indices; - vertices.clear(); - indices.clear(); - - float space_size = font_->GetLineHeight() * 0.3f; - - const float line_height = font_->GetLineHeight(); - size_ = glm::vec2(0.0f); - glm::vec2 cursor(0.0f); - - uint32_t cp = 0; - const char* p = text; - - uint32_t color = color_; - - - while (cp = DecodeUTF8Codepoint(p)) - { - 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))) - break; - - if (cp == 'r') // reset color - { - color = color_; - continue; - } - - color = 0; - - // parse color - for (size_t i = 0; i < 3; ++i) - { - color >>= 8; - uint32_t ch; - if (cp >= '0' && cp <= '9') - ch = cp - '0'; - else if (cp >= 'a' && cp <= 'f') - ch = cp - 'a' + 10; - else - break; - - color |= (ch << 16); - color |= (ch << 20); - - if (!(cp = DecodeUTF8Codepoint(p))) - break; - } - - color |= 0xFF000000; // alpha=1 - - //if (cp != ';') - // break; - - //continue; - } - - const FontGlyphData* glyph = font_->GetCodepointGlyph(cp); - - if (!glyph) - continue; // Dont even have "missing" glyph, font is shit - - glm::vec2 p0 = cursor + glyph->offset; - glm::vec2 p1 = p0 + glyph->size; - - uint32_t base_index = vertices.size(); - - PutVertex(vertices, glm::vec3(p0.x, -p0.y, 0.0f), glyph->uv0, color); - PutVertex(vertices, glm::vec3(p1.x, -p0.y, 0.0f), glm::vec2(glyph->uv1.x, glyph->uv0.y), color); - PutVertex(vertices, glm::vec3(p1.x, -p1.y, 0.0f), glyph->uv1, color); - PutVertex(vertices, glm::vec3(p0.x, -p1.y, 0.0f), glm::vec2(glyph->uv0.x, glyph->uv1.y), color); - - 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); - - cursor.x += glyph->advance; - - size_.x = glm::max(size_.x, cursor.x); - } - - size_.y = cursor.y + line_height; - - va_.SetVBOData(vertices.data(), vertices.size() * sizeof(TextVertex)); - va_.SetIndices(indices.data(), indices.size()); -} diff --git a/src/gfx/text.hpp b/src/gfx/text.hpp deleted file mode 100644 index 4f120fd..0000000 --- a/src/gfx/text.hpp +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -#include "font.hpp" -#include "vertex_array.hpp" - -namespace gfx -{ - -class Text -{ -public: - Text(std::shared_ptr font, uint32_t color); - - void SetText(const char* text); - void SetText(const std::string& text) { SetText(text.c_str()); } - - const std::shared_ptr& GetFont() const { return font_; } - const VertexArray& GetVA() const { return va_; } - const glm::vec2& GetSize() const { return size_; } - -private: - std::shared_ptr font_; - VertexArray va_; - uint32_t color_; - glm::vec2 size_; // size of the text in pixels -}; - -} // namespace gfx \ No newline at end of file diff --git a/src/gui/context.cpp b/src/gui/context.cpp new file mode 100644 index 0000000..39c4078 --- /dev/null +++ b/src/gui/context.cpp @@ -0,0 +1,249 @@ +#include "context.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) +{ + +} + +void gui::Context::Begin() +{ + vertices_.clear(); + indices_.clear(); + ranges_.clear(); +} + +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 = 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(); + float space_size = 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 + break; + + 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; + glm::vec2 p1 = p0 + glyph->size * scale; + + PushRect(p0, glyph->uv0, p1, glyph->uv1, curr_color); + cursor.x += glyph->advance * scale; + } +} + +void gui::Context::Render() +{ + va_.SetVBOData(vertices_.data(), vertices_.size() * sizeof(vertices_[0])); + va_.SetIndices(indices_.data(), indices_.size()); + + static const gfx::HudPosition pos; + + for (const auto& range : ranges_) + { + gfx::DrawHudCmd hudcmd; + hudcmd.va = &va_; + hudcmd.pos = &pos; + 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(); + 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 +} diff --git a/src/gui/context.hpp b/src/gui/context.hpp new file mode 100644 index 0000000..cb0b58e --- /dev/null +++ b/src/gui/context.hpp @@ -0,0 +1,63 @@ +#pragma once + +#include "font.hpp" +#include "gfx/vertex_array.hpp" +#include "gfx/draw_list.hpp" + +namespace gui +{ + +struct GuiVertex +{ + glm::vec3 pos; + uint32_t color; + glm::vec2 uv; +}; + +struct GuiRange +{ + size_t start; + size_t count; + const gfx::Texture* texture; +}; + +struct Rect +{ + glm::vec2 min; + glm::vec2 max; +}; + +class Context +{ +public: + Context(gfx::DrawList& dlist, std::shared_ptr default_font); + + void Begin(); + + glm::vec2 MeasureText(std::string_view text); + void DrawText(std::string_view text, const glm::vec2& pos, uint32_t color = 0xFFFFFFFF, float scale = 1.0f); + + void Render(); + + const std::shared_ptr& GetFont() const { return font_; } + +private: + void BeginTexture(const gfx::Texture* texture); + void PushRect(const glm::vec2& p0, const glm::vec2& uv0, const glm::vec2& p1, const glm::vec2& uv1, uint32_t color); + +private: + gfx::DrawList& dlist_; + std::shared_ptr font_; + gfx::VertexArray va_; + + // building + std::vector vertices_; + std::vector indices_; + std::vector ranges_; + + +}; + + + +} \ No newline at end of file diff --git a/src/gfx/font.cpp b/src/gui/font.cpp similarity index 95% rename from src/gfx/font.cpp rename to src/gui/font.cpp index fbef9f0..7b0f282 100644 --- a/src/gfx/font.cpp +++ b/src/gui/font.cpp @@ -3,7 +3,7 @@ #include "assets/cache.hpp" #include "assets/cmdfile.hpp" -std::shared_ptr gfx::Font::LoadFromFile(const std::string& path) +std::shared_ptr gui::Font::LoadFromFile(const std::string& path) { auto font = std::make_shared(); diff --git a/src/gfx/font.hpp b/src/gui/font.hpp similarity index 89% rename from src/gfx/font.hpp rename to src/gui/font.hpp index 02c1420..b48b8d3 100644 --- a/src/gfx/font.hpp +++ b/src/gui/font.hpp @@ -6,9 +6,9 @@ #include -#include "texture.hpp" +#include "gfx/texture.hpp" -namespace gfx +namespace gui { using Codepoint = uint32_t; @@ -87,12 +87,12 @@ public: return data ? data : glyphs_.Get(0); // try return missing codepoint glyph if missing } - const std::shared_ptr& GetTexture() const { return texture_; } + const std::shared_ptr& GetTexture() const { return texture_; } float GetLineHeight() const { return line_height_; } private: - std::shared_ptr texture_; // Texture atlas for the font + std::shared_ptr texture_; // Texture atlas for the font CodepointMap glyphs_; float line_height_ = 0.0f; // Line height in pixels, used for text layout