fekalnigtacko/src/gui/context.cpp

263 lines
6.8 KiB
C++

#include "context.hpp"
#include "assets/cache.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)
{
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
}