Immediate gui

This commit is contained in:
zbyv 2026-03-05 23:09:49 +01:00
parent dfe2b8a8c8
commit 53f64b8513
17 changed files with 370 additions and 292 deletions

View File

@ -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"
)

View File

@ -10,7 +10,7 @@
#ifdef CLIENT
#include "audio/sound.hpp"
#include "gfx/texture.hpp"
#include "gfx/font.hpp"
#include "gui/font.hpp"
#endif
#include <iostream>
@ -61,10 +61,10 @@ protected:
PtrType Load(const std::string& key) override { return audio::Sound::LoadFromFile(key); }
};
class FontCache final : public Cache<gfx::Font>
class FontCache final : public Cache<gui::Font>
{
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<const gfx::Font> GetFont(const std::string& filename)
static std::shared_ptr<const gui::Font> GetFont(const std::string& filename)
{
return font_cache_.Get(filename);
}

View File

@ -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<gfx::Text>(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<float>(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);
}
}

View File

@ -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<gfx::Text> 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<game::view::ClientSession> session_;
std::shared_ptr<const gfx::Font> font_;
std::deque<ChatMessage> chat_;
std::vector<gfx::HudPosition> chatpos_;
};

View File

@ -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);

View File

@ -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:

View File

@ -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)
{
}
};

View File

@ -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)

View File

@ -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;

View File

@ -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

View File

@ -150,8 +150,8 @@ void gfx::Renderer::DrawSurfaceList(std::span<DrawSurfaceCmd> 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<DrawSurfaceCmd> 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<DrawHudCmd> queue, const DrawListParam
last_vao = cmd.va;
}
glDrawElements(GL_TRIANGLES, static_cast<GLsizei>(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<GLsizei>(count), GL_UNSIGNED_INT, (void*)(first * sizeof(GLuint)));
}
}

View File

@ -1,175 +0,0 @@
#include "text.hpp"
#include "utils/bufferput.hpp"
gfx::Text::Text(std::shared_ptr<const Font> 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<TextVertex>& 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<TextVertex> vertices;
static std::vector<uint32_t> 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());
}

View File

@ -1,28 +0,0 @@
#pragma once
#include "font.hpp"
#include "vertex_array.hpp"
namespace gfx
{
class Text
{
public:
Text(std::shared_ptr<const Font> font, uint32_t color);
void SetText(const char* text);
void SetText(const std::string& text) { SetText(text.c_str()); }
const std::shared_ptr<const Font>& GetFont() const { return font_; }
const VertexArray& GetVA() const { return va_; }
const glm::vec2& GetSize() const { return size_; }
private:
std::shared_ptr<const Font> font_;
VertexArray va_;
uint32_t color_;
glm::vec2 size_; // size of the text in pixels
};
} // namespace gfx

249
src/gui/context.cpp Normal file
View File

@ -0,0 +1,249 @@
#include "context.hpp"
gui::Context::Context(gfx::DrawList& dlist, std::shared_ptr<const Font> 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
}

63
src/gui/context.hpp Normal file
View File

@ -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<const Font> 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<const Font>& 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<const Font> font_;
gfx::VertexArray va_;
// building
std::vector<GuiVertex> vertices_;
std::vector<uint32_t> indices_;
std::vector<GuiRange> ranges_;
};
}

View File

@ -3,7 +3,7 @@
#include "assets/cache.hpp"
#include "assets/cmdfile.hpp"
std::shared_ptr<const gfx::Font> gfx::Font::LoadFromFile(const std::string& path)
std::shared_ptr<const gui::Font> gui::Font::LoadFromFile(const std::string& path)
{
auto font = std::make_shared<Font>();

View File

@ -6,9 +6,9 @@
#include <glm/glm.hpp>
#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<const Texture>& GetTexture() const { return texture_; }
const std::shared_ptr<const gfx::Texture>& GetTexture() const { return texture_; }
float GetLineHeight() const { return line_height_; }
private:
std::shared_ptr<const Texture> texture_; // Texture atlas for the font
std::shared_ptr<const gfx::Texture> texture_; // Texture atlas for the font
CodepointMap<FontGlyphData> glyphs_;
float line_height_ = 0.0f; // Line height in pixels, used for text layout