diff options
| author | fschildt <florian.schildt@protonmail.com> | 2025-10-16 15:33:06 +0200 |
|---|---|---|
| committer | fschildt <florian.schildt@protonmail.com> | 2025-10-16 15:33:06 +0200 |
| commit | a873df7a66dc1831cee4eae2d998abed88246268 (patch) | |
| tree | c19cd079ce106e1431d64c34babf4ef59cf71723 /src | |
| parent | 9f2845b12135c32dde91e58afc1193d54333ec9f (diff) | |
renderer: introduce text rendering
Diffstat (limited to 'src')
| -rw-r--r-- | src/common/Font.cpp | 40 | ||||
| -rw-r--r-- | src/common/Font.hpp | 6 | ||||
| -rw-r--r-- | src/games/Game.cpp | 4 | ||||
| -rw-r--r-- | src/games/Game.hpp | 7 | ||||
| -rw-r--r-- | src/games/minesweeper/Minesweeper.cpp | 40 | ||||
| -rw-r--r-- | src/games/minesweeper/Minesweeper.hpp | 7 | ||||
| -rw-r--r-- | src/games/snake/Snake.cpp | 19 | ||||
| -rw-r--r-- | src/games/tetris/Board.cpp | 4 | ||||
| -rw-r--r-- | src/games/tetris/Tetris.cpp | 131 | ||||
| -rw-r--r-- | src/games/tetris/Tetris.hpp | 6 | ||||
| -rw-r--r-- | src/games/tetris/Tetromino.cpp | 2 | ||||
| -rw-r--r-- | src/renderer/RSoftwareBackend.cpp | 123 | ||||
| -rw-r--r-- | src/renderer/RSoftwareBackend.hpp | 13 | ||||
| -rw-r--r-- | src/renderer/Renderer.cpp | 47 | ||||
| -rw-r--r-- | src/renderer/Renderer.hpp | 23 |
15 files changed, 302 insertions, 170 deletions
diff --git a/src/common/Font.cpp b/src/common/Font.cpp index 82f6ad8..8f058ca 100644 --- a/src/common/Font.cpp +++ b/src/common/Font.cpp @@ -12,30 +12,24 @@ is_ch_ascii(char32_t ch) return result; } -Font::~Font() -{ - delete[] m_file_content; -} - -bool -Font::Init(const char* path, int font_size) +Font::Font(const char *path, int font_size) { if (!ReadFile(path)) { - return false; + return; } - // set font info + // init m_file_content, m_font_info if (!stbtt_InitFont(&m_font_info, (unsigned char*)m_file_content, 0)) { std::cout << "stbtt_InitFont failed.\n"; delete[] m_file_content; m_file_content = nullptr; - return false; + return; } - // set font settings (scale + vmetrics) + // init font settings float scale = stbtt_ScaleForPixelHeight(&m_font_info, (float)font_size); int baseline, ascent, descent, line_gap; stbtt_GetFontVMetrics(&m_font_info, &ascent, &descent, &line_gap); @@ -44,8 +38,6 @@ Font::Init(const char* path, int font_size) descent = int(scale * (float)descent); line_gap = int(scale * (float)line_gap); - - // init members m_font_scale = scale; m_font_baseline = baseline; m_font_yadvance = ascent - descent + line_gap; @@ -53,12 +45,14 @@ Font::Init(const char* path, int font_size) // load glyphs for (char c = first_ascii_ch; c <= last_ascii_ch; ++c) { - LoadGlyph(m_glyphs[c-first_ascii_ch], static_cast<char32_t>(c)); + InitGlyph(m_glyphs[c-first_ascii_ch], static_cast<char32_t>(c)); } memset((void*)&m_fail_glyph, 0, sizeof(m_fail_glyph)); +} - - return true; +Font::~Font() +{ + delete[] m_file_content; } bool @@ -83,7 +77,7 @@ Font::ReadFile(const char* path) } void -Font::LoadGlyph(Glyph& glyph, char32_t c) +Font::InitGlyph(Glyph& glyph, char32_t c) { int bbx0, bby0, bbx1, bby1; stbtt_GetCodepointBitmapBox(&m_font_info, (int)c, m_font_scale, m_font_scale, &bbx0, &bby0, &bbx1, &bby1); @@ -130,8 +124,10 @@ Font::LoadGlyph(Glyph& glyph, char32_t c) AlphaBitmap& Font::GetAlphaBitmap(char32_t c) { - if (is_ch_ascii(c)) { - return m_glyphs[c - first_ascii_ch].bitmap; + if (m_file_content) { + if (is_ch_ascii(c)) { + return m_glyphs[c - first_ascii_ch].bitmap; + } } return m_fail_glyph.bitmap; @@ -140,8 +136,10 @@ Font::GetAlphaBitmap(char32_t c) Glyph& Font::GetGlyph(char32_t c) { - if (is_ch_ascii(c)) { - return m_glyphs[c - first_ascii_ch]; + if (m_file_content) { + if (is_ch_ascii(c)) { + return m_glyphs[c - first_ascii_ch]; + } } return m_fail_glyph; diff --git a/src/common/Font.hpp b/src/common/Font.hpp index af4ddb5..876b03a 100644 --- a/src/common/Font.hpp +++ b/src/common/Font.hpp @@ -23,9 +23,9 @@ struct Glyph { class Font { public: + explicit Font(const char* path, int size); ~Font(); - bool Init(const char* path, int font_size); AlphaBitmap& GetAlphaBitmap(char32_t c); Glyph& GetGlyph(char32_t c); @@ -33,7 +33,7 @@ public: private: bool ReadFile(const char* path); - void LoadGlyph(Glyph& glyph, char32_t c); + void InitGlyph(Glyph& glyph, char32_t c); private: @@ -43,12 +43,12 @@ private: const char* m_file_content = nullptr; + stbtt_fontinfo m_font_info; float m_font_scale; int m_font_baseline; int m_font_yadvance; - stbtt_fontinfo m_font_info; Glyph m_glyphs[ascii_glyph_count]; Glyph m_fail_glyph; }; diff --git a/src/games/Game.cpp b/src/games/Game.cpp index 0e6af1c..c403a40 100644 --- a/src/games/Game.cpp +++ b/src/games/Game.cpp @@ -50,7 +50,7 @@ Game::ProcessDt() } void -Game::DrawGameOverMenu() +Game::DrawDefaultGameOverMenu() { ImGui::Begin("DefaultGameOverMenu", nullptr, s_imgui_window_flags_menu); ImGui::Text("Game Over."); @@ -64,7 +64,7 @@ Game::DrawGameOverMenu() } void -Game::DrawGamePausedMenu() +Game::DrawDefaultGamePausedMenu() { ImGui::Begin("DefaultGamePauseMenu", nullptr, s_imgui_window_flags_menu); if (ImGui::Button("Resume")) { diff --git a/src/games/Game.hpp b/src/games/Game.hpp index 7de3b66..a020012 100644 --- a/src/games/Game.hpp +++ b/src/games/Game.hpp @@ -35,8 +35,8 @@ public: protected: - void DrawGameOverMenu(); - void DrawGamePausedMenu(); + void DrawDefaultGameOverMenu(); + void DrawDefaultGamePausedMenu(); float ProcessDt(); @@ -46,6 +46,9 @@ protected: protected: + static constexpr const char* s_dejavu_sans_filepath = "./fonts/dejavu_ttf/DejaVuSans.ttf"; + static constexpr const char* s_dejavu_sans_mono_filepath = "./fonts/dejavu_ttf/DejaVuSansMono.ttf"; + static constexpr ImGuiWindowFlags s_imgui_window_flags_menu = ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_AlwaysAutoResize; static constexpr ImGuiWindowFlags s_imgui_window_flags_default = ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoScrollbar; }; diff --git a/src/games/minesweeper/Minesweeper.cpp b/src/games/minesweeper/Minesweeper.cpp index 2a69e4d..a091491 100644 --- a/src/games/minesweeper/Minesweeper.cpp +++ b/src/games/minesweeper/Minesweeper.cpp @@ -1,4 +1,3 @@ -#include "games/Game.hpp" #include <games/minesweeper/Minesweeper.hpp> #include <renderer/Renderer.hpp> @@ -6,10 +5,15 @@ #include <algorithm> #include <random> -#include <cstdio> -// Todo: winning condition (maybe: total_cells - uncovered_cells = mine_count) +// Todo: +// - First click may not be a mine! +// - Show current time spent +// - Show current flags/mines left +// - Permit uncovering of flagged cell +// - Game Over: Winning Condition (maybe: total_cells - uncovered_cells == mine_count) +// - Game Over: Show Highscore for selected difficulty static constexpr Color s_mine_count_colors[8] = { @@ -25,11 +29,8 @@ static constexpr Color s_mine_count_colors[8] = { Minesweeper::Minesweeper() + : m_font{s_dejavu_sans_filepath, 22} { - bool font_init = m_font.Init(s_font_filepath, 22); - if (!font_init) { - printf("m_font.Init(...) failed\n"); - } } void @@ -148,7 +149,7 @@ Minesweeper::Update(std::vector<SDL_Event>& events) if (m_game_status == game_paused) { DrawBoard(); - DrawGamePausedMenu(); + DrawDefaultGamePausedMenu(); } else if (m_game_status == game_starting) { DrawStartMenu(); @@ -158,7 +159,7 @@ Minesweeper::Update(std::vector<SDL_Event>& events) } else if (m_game_status == game_over) { DrawBoard(); - DrawGameOverMenu(); + DrawDefaultGameOverMenu(); } return true; @@ -370,7 +371,7 @@ Minesweeper::DrawBoard() bool is_mine = IsMine(x, y); if (is_covered) { - g_renderer.PushRectangle(cell_rect, 0.0f, covered_cell_color); + g_renderer.PushRectangle(cell_rect, covered_cell_color, 0.0f); if (is_flagged) { @@ -385,7 +386,7 @@ Minesweeper::DrawBoard() flag_pos.x + flag_size.x, flag_pos.y + flag_size.y, }; - g_renderer.PushRectangle(flag_rect, flag_pos.z, flag_color); + g_renderer.PushRectangle(flag_rect, flag_color, flag_pos.z); } } else { @@ -401,26 +402,21 @@ Minesweeper::DrawBoard() mine_pos.x + m_cell_inner_size.x, mine_pos.y + m_cell_inner_size.y, }; - g_renderer.PushRectangle(mine_rect, mine_pos.z, mine_color); + g_renderer.PushRectangle(mine_rect, mine_color, mine_pos.z); } else { - g_renderer.PushRectangle(cell_rect, 0.0f, uncovered_cell_color); + g_renderer.PushRectangle(cell_rect, uncovered_cell_color, 0.0f); - // Todo: Figure out how to scale this properly. - // 256.0f is a random number that just works for now. V3F32 mine_count_pos = { - 256.0f * cell_pos.x, - 256.0f * cell_pos.y, - 256.0f + cell_pos.x, + cell_pos.y, + 1.0f }; int32_t mine_count = m_adjacent_mine_counts[y*m_grid_width + x]; if (mine_count > 0) { Color color = s_mine_count_colors[mine_count-1]; Glyph& glyph = m_font.GetGlyph('0' + (char32_t)mine_count); - g_renderer.PushAlphaBitmap( - glyph.bitmap, - mine_count_pos, - color); + g_renderer.PushAlphaBitmap(glyph.bitmap, mine_count_pos, color); } } } diff --git a/src/games/minesweeper/Minesweeper.hpp b/src/games/minesweeper/Minesweeper.hpp index 24672f0..fbdcb30 100644 --- a/src/games/minesweeper/Minesweeper.hpp +++ b/src/games/minesweeper/Minesweeper.hpp @@ -4,8 +4,6 @@ #include <common/math.hpp> #include <common/Font.hpp> -#include <array> - class Minesweeper : public Game { public: @@ -21,11 +19,11 @@ public: bool Update(std::vector<SDL_Event>& events) override; + +private: void ProcessEventDuringPause(SDL_Event& event); void ProcessEventDuringResume(SDL_Event& event); - -private: void Reset(Difficulty Difficulty); void InitIsMineBitmap(int32_t mine_count); void InitAdjacentMineCounters(); @@ -50,7 +48,6 @@ private: private: static constexpr int32_t max_map_height = 32; static constexpr int32_t max_map_width = 32; - static constexpr const char* s_font_filepath = "./fonts/dejavu_ttf/DejaVuSans.ttf"; private: diff --git a/src/games/snake/Snake.cpp b/src/games/snake/Snake.cpp index 7969616..542122e 100644 --- a/src/games/snake/Snake.cpp +++ b/src/games/snake/Snake.cpp @@ -1,10 +1,13 @@ -#include "common/defs.hpp" -#include <chrono> #include <games/snake/Snake.hpp> #include <renderer/Renderer.hpp> #include <imgui.h> +// Todo: +// - gradiant from head to tail +// - reduce thickness from head to tail + + std::mt19937 Snake::s_rng{std::random_device{}()}; @@ -77,10 +80,10 @@ Snake::Update(std::vector<SDL_Event> &events) MaybeMoveSnake(dt); } break; case game_over: { - DrawGameOverMenu(); + DrawDefaultGameOverMenu(); } break; case game_paused: { - DrawGamePausedMenu(); + DrawDefaultGamePausedMenu(); } break; case game_exit: { return false; @@ -303,7 +306,7 @@ Snake::Draw() map_world_pos.y + map_world_dim.y }; Color bg_color = {0.0f, 0.0f, 0.0f, 1.0f}; - g_renderer.PushRectangle(map_world_rect, map_world_pos.z, bg_color); + g_renderer.PushRectangle(map_world_rect, bg_color, map_world_pos.z); /* draw snake */ @@ -332,7 +335,7 @@ Snake::Draw() }; Color color = {0.3f, 0.3f, 0.3f, 1.0f}; - g_renderer.PushRectangle(world_rect, world_pos.z, color); + g_renderer.PushRectangle(world_rect, color, world_pos.z); tail++; } tail = 0; @@ -360,7 +363,7 @@ Snake::Draw() }; Color color = {0.3f, 0.3f, 0.3f, 1.0f}; - g_renderer.PushRectangle(world_rect, world_pos.z, color); + g_renderer.PushRectangle(world_rect, color, world_pos.z); tail++; } @@ -379,6 +382,6 @@ Snake::Draw() pos.y + dim.y }; Color color = {0.3f, 0.6f, 0.4f, 1.0f}; - g_renderer.PushRectangle(rect, pos.z, color); + g_renderer.PushRectangle(rect, color, pos.z); } diff --git a/src/games/tetris/Board.cpp b/src/games/tetris/Board.cpp index 1344d5b..7f391b4 100644 --- a/src/games/tetris/Board.cpp +++ b/src/games/tetris/Board.cpp @@ -115,7 +115,7 @@ Board::Draw(int32_t level) bg_world_pos.y + bg_world_dim.y, }; Color bg_color = {0.0f, 0.0f, 0.0f, 1.0f}; - g_renderer.PushRectangle(bg_world_rect, bg_world_pos.z, bg_color); + g_renderer.PushRectangle(bg_world_rect, bg_color, bg_world_pos.z); // tetromino parts @@ -145,7 +145,7 @@ Board::Draw(int32_t level) Color color = Tetromino::GetColor(tetromino_id); - g_renderer.PushRectangle(world_rect, world_pos.z, color); + g_renderer.PushRectangle(world_rect, color, world_pos.z); } } } diff --git a/src/games/tetris/Tetris.cpp b/src/games/tetris/Tetris.cpp index aefdb41..aeb0c99 100644 --- a/src/games/tetris/Tetris.cpp +++ b/src/games/tetris/Tetris.cpp @@ -8,11 +8,23 @@ #include <imgui.h> #include <fstream> +#include <iostream> + + +static +std::u32string +int32_to_u32string(int32_t value) +{ + std::string str = std::to_string(value); + return std::u32string(str.begin(), str.end()); +} + Tetris::Tetris() - : m_font{} + : m_font{s_dejavu_sans_mono_filepath, 22} , m_active_tetromino{m_board.m_bitmap} { + m_frame_strings.reserve(s_frame_strings_capcity); } void @@ -42,6 +54,8 @@ Tetris::Update(std::vector<SDL_Event>& events) g_renderer.SetCameraSize(4.0f, 3.0f); g_renderer.Clear(clear_color); + m_frame_strings.clear(); + if (m_game_status == game_starting) { Start(); @@ -52,7 +66,7 @@ Tetris::Update(std::vector<SDL_Event>& events) if (m_game_status == game_resuming) { - uint32_t softdrop_count = GetSoftdropCount(dt); // side-effect: m_dt_remaining_seconds + uint32_t softdrop_count = GetSoftdropCount(dt); for (uint32_t i{0}; i < softdrop_count; i++) { bool moved_down = m_active_tetromino.MaybeMoveDown(); if (!moved_down) { @@ -70,6 +84,7 @@ Tetris::Update(std::vector<SDL_Event>& events) } } + assert(m_frame_strings.capacity() == s_frame_strings_capcity); if (m_game_status == game_exit) { return false; @@ -232,7 +247,7 @@ Tetris::Draw() DrawScore(); if (m_game_status == game_paused) { - DrawGamePausedMenu(); + DrawDefaultGamePausedMenu(); } else if (m_game_status == game_over) { DrawGameOverMenu(); @@ -257,77 +272,87 @@ Tetris::DrawGameOverMenu() void Tetris::DrawLineCounter() { - V2F32 view_pos = {0.5f, 2.6f}; - ImVec2 screen_pos = g_renderer.ViewPosToScreenPosImGui(view_pos); + static std::u32string text = U"Lines: xxx"; + V3F32 pos = {0.5f, 2.6f, 4.0f}; + Color color = {0.9f, 0.9f, 0.9f, 1.0f}; - ImGui::SetNextWindowPos(screen_pos); - ImGui::Begin("TetrisLines", nullptr, s_imgui_window_flags_default); - ImGui::Text("LINES - %d", m_line_counter); - ImGui::End(); + int line_count = std::min(m_line_counter, 999); + + text[9] = U'0' + char32_t(line_count % 10); + line_count /= 10; + text[8] = U'0' + char32_t(line_count % 10); + line_count /= 10; + text[7] = U'0' + char32_t(line_count % 10); + line_count /= 10; + + + g_renderer.PushText(text, m_font, pos, color); + pos.x += 0.2f; } void Tetris::DrawStatistics() { - V2F32 view_tetrominoes_pos = {0.4f, 1.8f}; - V2F32 view_advance = {0.0f, 0.2f}; + V2F32 pos = {0.4f, 0.5f}; - V2F32 view_text_title_pos = view_tetrominoes_pos + V2F32(0.02f, 0.4f); - ImVec2 screen_text_title_pos = g_renderer.ViewPosToScreenPosImGui(view_text_title_pos); - V2F32 view_text_pos = view_tetrominoes_pos + V2F32(0.4f, 0.16f); - V2F32 view_text_gap = {0.0f, 0.124f}; - ImVec2 screen_text_pos = g_renderer.ViewPosToScreenPosImGui(view_text_pos); - ImVec2 screen_text_gap = g_renderer.ViewSizeToScreenSizeImGui(view_text_gap); + static std::u32string title_text = U"Statistics"; + V3F32 title_pos = {pos.x + 0.02f, pos.y + 1.64f, s_text_z}; + g_renderer.PushText(title_text, m_font, title_pos, s_text_color); - Tetromino::Draw(Tetromino::t_piece, 0, view_tetrominoes_pos, 0.5f); - view_tetrominoes_pos.y -= view_advance.y; + float yadvance = -0.2f; + float tetrominoes_x0 = pos.x; + float tetrominoes_y0 = pos.y - (float)Tetromino::id_count * yadvance; + V2F32 tetromino_pos = {tetrominoes_x0, tetrominoes_y0}; - Tetromino::Draw(Tetromino::j_piece, 0, view_tetrominoes_pos, 0.5f); - view_tetrominoes_pos.y -= view_advance.y; + Tetromino::Draw(Tetromino::t_piece, 0, tetromino_pos, 0.5f); + tetromino_pos.y += yadvance; - Tetromino::Draw(Tetromino::z_piece, 0, view_tetrominoes_pos, 0.5f); - view_tetrominoes_pos.y -= view_advance.y; + Tetromino::Draw(Tetromino::j_piece, 0, tetromino_pos, 0.5f); + tetromino_pos.y += yadvance; - Tetromino::Draw(Tetromino::o_piece, 0, view_tetrominoes_pos, 0.5f); - view_tetrominoes_pos.y -= view_advance.y; + Tetromino::Draw(Tetromino::z_piece, 0, tetromino_pos, 0.5f); + tetromino_pos.y += yadvance; - Tetromino::Draw(Tetromino::s_piece, 0, view_tetrominoes_pos, 0.5f); - view_tetrominoes_pos.y -= view_advance.y; + Tetromino::Draw(Tetromino::o_piece, 0, tetromino_pos, 0.5f); + tetromino_pos.y += yadvance; - Tetromino::Draw(Tetromino::l_piece, 0, view_tetrominoes_pos, 0.5f); - view_tetrominoes_pos.y -= view_advance.y; + Tetromino::Draw(Tetromino::s_piece, 0, tetromino_pos, 0.5f); + tetromino_pos.y += yadvance; - Tetromino::Draw(Tetromino::i_piece, 0, view_tetrominoes_pos, 0.5f); - view_tetrominoes_pos.y -= view_advance.y; + Tetromino::Draw(Tetromino::l_piece, 0, tetromino_pos, 0.5f); + tetromino_pos.y += yadvance; + Tetromino::Draw(Tetromino::i_piece, 0, tetromino_pos, 0.5f); + tetromino_pos.y += yadvance; - ImGui::SetNextWindowPos(screen_text_title_pos); - ImGui::Begin("TetrisStatisticsTitle", nullptr, s_imgui_window_flags_default); - ImGui::Text("STATISTICS"); - ImGui::End(); + float counters_x0 = pos.x + 0.4f; + float counters_y0 = pos.y + 0.05f - yadvance * (float)Tetromino::id_count; + V3F32 counters_pos = {counters_x0, counters_y0, s_text_z}; - ImGui::SetNextWindowPos(screen_text_pos); - ImGui::Begin("TetrisStatistics", nullptr, s_imgui_window_flags_default); - - ImGui::Text("%d", m_tetromino_counters[Tetromino::t_piece]); - ImGui::Dummy(screen_text_gap); - ImGui::Text("%d", m_tetromino_counters[Tetromino::j_piece]); - ImGui::Dummy(screen_text_gap); - ImGui::Text("%d", m_tetromino_counters[Tetromino::z_piece]); - ImGui::Dummy(screen_text_gap); - ImGui::Text("%d", m_tetromino_counters[Tetromino::o_piece]); - ImGui::Dummy(screen_text_gap); - ImGui::Text("%d", m_tetromino_counters[Tetromino::s_piece]); - ImGui::Dummy(screen_text_gap); - ImGui::Text("%d", m_tetromino_counters[Tetromino::l_piece]); - ImGui::Dummy(screen_text_gap); - ImGui::Text("%d", m_tetromino_counters[Tetromino::i_piece]); - ImGui::Dummy(screen_text_gap); + std::u32string& t_count = m_frame_strings.emplace_back(int32_to_u32string(m_tetromino_counters[Tetromino::t_piece])); + std::u32string& j_count = m_frame_strings.emplace_back(int32_to_u32string(m_tetromino_counters[Tetromino::j_piece])); + std::u32string& z_count = m_frame_strings.emplace_back(int32_to_u32string(m_tetromino_counters[Tetromino::z_piece])); + std::u32string& o_count = m_frame_strings.emplace_back(int32_to_u32string(m_tetromino_counters[Tetromino::o_piece])); + std::u32string& s_count = m_frame_strings.emplace_back(int32_to_u32string(m_tetromino_counters[Tetromino::s_piece])); + std::u32string& l_count = m_frame_strings.emplace_back(int32_to_u32string(m_tetromino_counters[Tetromino::l_piece])); + std::u32string& i_count = m_frame_strings.emplace_back(int32_to_u32string(m_tetromino_counters[Tetromino::i_piece])); - ImGui::End(); + g_renderer.PushText(t_count, m_font, counters_pos, s_text_color); + counters_pos.y += yadvance; + g_renderer.PushText(j_count, m_font, counters_pos, s_text_color); + counters_pos.y += yadvance; + g_renderer.PushText(z_count, m_font, counters_pos, s_text_color); + counters_pos.y += yadvance; + g_renderer.PushText(o_count, m_font, counters_pos, s_text_color); + counters_pos.y += yadvance; + g_renderer.PushText(s_count, m_font, counters_pos, s_text_color); + counters_pos.y += yadvance; + g_renderer.PushText(l_count, m_font, counters_pos, s_text_color); + counters_pos.y += yadvance; + g_renderer.PushText(i_count, m_font, counters_pos, s_text_color); } void diff --git a/src/games/tetris/Tetris.hpp b/src/games/tetris/Tetris.hpp index 03966fd..7f48e37 100644 --- a/src/games/tetris/Tetris.hpp +++ b/src/games/tetris/Tetris.hpp @@ -31,6 +31,10 @@ private: private: + static constexpr Color s_text_color {0.9f, 0.9f, 0.9f, 1.0f}; + static constexpr float s_text_z {10.0f}; + static constexpr size_t s_frame_strings_capcity {32}; + Font m_font; Board m_board; Tetromino m_active_tetromino; @@ -43,6 +47,8 @@ private: int32_t m_level = 0; int32_t m_softdrop_counter = 0; int32_t m_highscore = 0; + + std::vector<std::u32string> m_frame_strings; }; diff --git a/src/games/tetris/Tetromino.cpp b/src/games/tetris/Tetromino.cpp index 2cde231..416a8df 100644 --- a/src/games/tetris/Tetromino.cpp +++ b/src/games/tetris/Tetromino.cpp @@ -233,7 +233,7 @@ Tetromino::Draw(Id id, int32_t ori, V2F32 pos, float scale) Color color = GetColor(id); - g_renderer.PushRectangle(world_rect, world_pos.z, color); + g_renderer.PushRectangle(world_rect, color, world_pos.z); } } } diff --git a/src/renderer/RSoftwareBackend.cpp b/src/renderer/RSoftwareBackend.cpp index 4d78ec7..d05cee7 100644 --- a/src/renderer/RSoftwareBackend.cpp +++ b/src/renderer/RSoftwareBackend.cpp @@ -1,3 +1,4 @@ +#include "imgui.h" #include <renderer/RSoftwareBackend.hpp> #include <SDL3/SDL_video.h> @@ -8,9 +9,8 @@ #include <cstdio> -RSoftwareBackend::RSoftwareBackend(SDL_Window* window, Renderer& renderer) - : m_window {window} - , m_renderer {renderer} +RSoftwareBackend::RSoftwareBackend(Renderer& renderer) + : m_renderer {renderer} { m_canvas.rshift = 0; m_canvas.gshift = 8; @@ -53,21 +53,7 @@ RSoftwareBackend::Draw() SortRenderEntities(); - REntity_Rectangle clear_rect = { - REntityType_Rectangle, - {0, 0, (float)(m_canvas.w-1), (float)(m_canvas.h-1)}, - 0.0f, - m_renderer.m_clear_color - }; - DrawRectangle(clear_rect); - - - float z = -1; for (RSortEntry sort_entry : m_renderer.m_sort_entries) { - if (sort_entry.z >= z) { - z = sort_entry.z; - } - REntity& entity = m_renderer.m_render_entities[sort_entry.entity_index]; switch (entity.type) { case REntityType_Rectangle: { @@ -78,6 +64,13 @@ RSoftwareBackend::Draw() DrawAlphaBitmap(entity.bitmap); } break; + case REntityType_Text: { + DrawText(entity.text); + }; break; + + case REntityType_Circle: { + }; break; + default:; } } @@ -147,10 +140,10 @@ RSoftwareBackend::DrawRectangle(REntity_Rectangle& entity) void RSoftwareBackend::DrawAlphaBitmap(REntity_AlphaBitmap& entity) { - int32_t x0 = (int32_t)entity.pos.x; - int32_t y0 = (int32_t)entity.pos.y; - int32_t x1 = (int32_t)entity.pos.x + entity.bitmap.w - 1; - int32_t y1 = (int32_t)entity.pos.y + entity.bitmap.h - 1; + int32_t x0 = m_renderer.WorldXToScreenX(entity.pos.x); + int32_t y0 = m_renderer.WorldYToScreenY(entity.pos.y); + int32_t x1 = x0 + entity.bitmap.w - 1; + int32_t y1 = y0 + entity.bitmap.h - 1; int32_t cut_left = 0; int32_t cut_bot = 0; @@ -171,10 +164,6 @@ RSoftwareBackend::DrawAlphaBitmap(REntity_AlphaBitmap& entity) } - uint32_t rshift = m_canvas.rshift; - uint32_t gshift = m_canvas.gshift; - uint32_t bshift = m_canvas.bshift; - uint8_t* alpha_row = (uint8_t*)entity.bitmap.pixels.get() + (-cut_bot * entity.bitmap.w) + (-cut_left); uint32_t* rgba_row = m_canvas.pixels + y0 * m_canvas.w + x0; for (int32_t y = y0; y <= y1; y++) { @@ -193,9 +182,9 @@ RSoftwareBackend::DrawAlphaBitmap(REntity_AlphaBitmap& entity) float g1 = entity.color.g * alphaf; float b1 = entity.color.b * alphaf; - uint32_t r2 = uint32_t(r1 * 255.0f) << rshift; - uint32_t g2 = uint32_t(g1 * 255.0f) << gshift; - uint32_t b2 = uint32_t(b1 * 255.0f) << bshift; + uint32_t r2 = uint32_t(r1 * 255.0f) << m_canvas.rshift; + uint32_t g2 = uint32_t(g1 * 255.0f) << m_canvas.gshift; + uint32_t b2 = uint32_t(b1 * 255.0f) << m_canvas.bshift; uint32_t rgba_result = r2 | g2 | b2; *rgba = rgba_result; @@ -209,3 +198,81 @@ RSoftwareBackend::DrawAlphaBitmap(REntity_AlphaBitmap& entity) } } +void +RSoftwareBackend::DrawText(REntity_Text& entity) +{ + int32_t xscreen = m_renderer.WorldXToScreenX(entity.pos.x); + int32_t yscreen = m_renderer.WorldYToScreenY(entity.pos.y); + for (char32_t ch : entity.text) { + Glyph& glyph = entity.font.GetGlyph(ch); + + int32_t x = xscreen + glyph.xoff; + int32_t y = yscreen + glyph.yoff; + DrawTextGlyph(glyph, entity.color, x, y); + + xscreen += glyph.xadvance; + } +} + +void +RSoftwareBackend::DrawTextGlyph(Glyph& glyph, Color color, int32_t xscreen, int32_t yscreen) +{ + int32_t x0 = xscreen; + int32_t y0 = yscreen; + int32_t x1 = x0 + glyph.bitmap.w - 1; + int32_t y1 = y0 + glyph.bitmap.h - 1; + + int32_t cut_left = 0; + int32_t cut_bot = 0; + + if (x0 < 0) { + cut_left = x0; + x0 = 0; + } + if (y0 < 0) { + cut_bot = y0; + y0 = 0; + } + if (x1 >= m_canvas.w) { + x1 = m_canvas.w - 1; + } + if (y1 >= m_canvas.h) { + y1 = m_canvas.h - 1; + } + + + uint8_t* alpha_row = (uint8_t*)glyph.bitmap.pixels.get() + (-cut_bot * glyph.bitmap.w) + (-cut_left); + uint32_t* rgba_row = m_canvas.pixels + y0 * m_canvas.w + x0; + + for (int32_t y = y0; y <= y1; y++) { + uint8_t* alpha = alpha_row; + uint32_t* rgba = rgba_row; + for (int32_t x = x0; x <= x1; x++) { + if (*alpha == 0) { + alpha++; + rgba++; + continue; + } + + float alphaf = *alpha / 255.0f; + + float r1 = color.r * alphaf; + float g1 = color.g * alphaf; + float b1 = color.b * alphaf; + + uint32_t r2 = uint32_t(r1 * 255.0f) << m_canvas.rshift; + uint32_t g2 = uint32_t(g1 * 255.0f) << m_canvas.gshift; + uint32_t b2 = uint32_t(b1 * 255.0f) << m_canvas.bshift; + + uint32_t rgba_result = r2 | g2 | b2; + *rgba = rgba_result; + + alpha++; + rgba++; + } + + alpha_row += glyph.bitmap.w; + rgba_row += m_canvas.w; + } +} + diff --git a/src/renderer/RSoftwareBackend.hpp b/src/renderer/RSoftwareBackend.hpp index 89d6a52..3e5e17d 100644 --- a/src/renderer/RSoftwareBackend.hpp +++ b/src/renderer/RSoftwareBackend.hpp @@ -16,11 +16,19 @@ public: uint32_t* pixels; }; + struct DestRect { + int32_t x0; + int32_t y0; + int32_t x1; + int32_t y1; + }; + public: - RSoftwareBackend(SDL_Window* window, Renderer& renderer); + RSoftwareBackend(Renderer& renderer); void Draw(); + void Clear(Color color); private: @@ -29,10 +37,11 @@ private: void DrawRectangle(REntity_Rectangle& entity); void DrawAlphaBitmap(REntity_AlphaBitmap& entity); + void DrawText(REntity_Text& entity); + void DrawTextGlyph(Glyph& glyph, Color color, int32_t xscreen, int32_t yscreen); private: - SDL_Window* m_window{}; Renderer& m_renderer; uint32_t m_gltexture_id{}; diff --git a/src/renderer/Renderer.cpp b/src/renderer/Renderer.cpp index 4e449ba..956c488 100644 --- a/src/renderer/Renderer.cpp +++ b/src/renderer/Renderer.cpp @@ -13,16 +13,10 @@ Renderer g_renderer; void Renderer::Init(SDL_Window* window) { + m_window = window; m_render_entities.reserve(1024); m_sort_entries.reserve(1024); - - m_backend = std::make_unique<RSoftwareBackend>(window, *this); -} - -void -Renderer::Draw() -{ - m_backend->Draw(); + m_backend = std::make_unique<RSoftwareBackend>(*this); } void @@ -30,12 +24,16 @@ Renderer::Reset() { m_render_entities.clear(); m_sort_entries.clear(); - m_render_entities.reserve(1024); - m_sort_entries.reserve(1024); SetCameraSize(0.0f, 0.0f); } void +Renderer::Draw() +{ + m_backend->Draw(); +} + +void Renderer::SetScreenSize(int32_t w, int32_t h) { m_screen_w = w; @@ -66,7 +64,13 @@ Renderer::WorldYToScreenY(float world_y) void Renderer::Clear(Color color) { - m_clear_color = color; + Rectangle rect = { + 0.0f, + 0.0f, + m_camera_w, + m_camera_h + }; + PushRectangle(rect, color, -1.0f); } void @@ -74,37 +78,48 @@ Renderer::PushAlphaBitmap(AlphaBitmap& bitmap, V3F32 pos, Color color) { m_render_entities.emplace_back(REntity{.bitmap{ REntityType_AlphaBitmap, - pos, bitmap, + pos, color }}); m_sort_entries.emplace_back(pos.z, m_render_entities.size()-1); } void -Renderer::PushRectangle(Rectangle rect, float z, Color color) +Renderer::PushRectangle(Rectangle rect, Color color, float z) { m_render_entities.emplace_back(REntity{.rect{ REntityType_Rectangle, rect, - z, color }}); m_sort_entries.emplace_back(z, m_render_entities.size()-1); } void -Renderer::PushCircle(Circle circle, float z, Color color) +Renderer::PushCircle(Circle circle, Color color, float z) { m_render_entities.emplace_back(REntity{.circle{ REntityType_Circle, circle, - z, color }}); m_sort_entries.emplace_back(z, m_render_entities.size()-1); } +void +Renderer::PushText(std::u32string& text, Font& font, V3F32 pos, Color color) +{ + m_render_entities.emplace_back(REntity{.text{ + REntityType_Text, + text, + font, + pos, + color + }}); + m_sort_entries.emplace_back(pos.z, m_render_entities.size()-1); +} + /* temporary helper functions (from old RGroup api) */ diff --git a/src/renderer/Renderer.hpp b/src/renderer/Renderer.hpp index b825681..515e1da 100644 --- a/src/renderer/Renderer.hpp +++ b/src/renderer/Renderer.hpp @@ -22,34 +22,44 @@ enum REntityType : int32_t { REntityType_Rectangle, REntityType_AlphaBitmap, REntityType_Circle, + REntityType_Text, }; + struct REntity_AlphaBitmap { REntityType type; - V3F32 pos; AlphaBitmap& bitmap; + V3F32 pos; Color color; }; struct REntity_Rectangle { REntityType type; Rectangle rect; - float z; Color color; }; struct REntity_Circle { REntityType type; Circle circle; - float z; Color color; }; +struct REntity_Text { + REntityType type; + std::u32string& text; + Font& font; + V3F32 pos; + Color color; +}; + + union REntity { REntityType type; REntity_AlphaBitmap bitmap; REntity_Rectangle rect; REntity_Circle circle; + REntity_Text text; }; struct RSortEntry { @@ -69,9 +79,10 @@ public: void Reset(); void Clear(Color color); - void PushRectangle(Rectangle rect, float z, Color color); void PushAlphaBitmap(AlphaBitmap& bitmap, V3F32 pos, Color color); - void PushCircle(Circle circle, float z, Color color); + void PushRectangle(Rectangle rect, Color color, float z); + void PushCircle(Circle circle, Color color, float z); + void PushText(std::u32string& text, Font& font, V3F32 pos, Color color); /* helper functions */ @@ -98,6 +109,8 @@ public: public: + SDL_Window* m_window; + int32_t m_screen_w; int32_t m_screen_h; |
