diff options
| -rw-r--r-- | cmake/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | src/basic/math.cpp | 64 | ||||
| -rw-r--r-- | src/basic/math.hpp | 11 | ||||
| -rw-r--r-- | src/games/Game.cpp | 8 | ||||
| -rw-r--r-- | src/games/Game.hpp | 3 | ||||
| -rw-r--r-- | src/games/minesweeper/Minesweeper.cpp | 32 | ||||
| -rw-r--r-- | src/games/minesweeper/Minesweeper.hpp | 9 | ||||
| -rw-r--r-- | src/games/snake/Snake.cpp | 16 | ||||
| -rw-r--r-- | src/games/snake/Snake.hpp | 6 | ||||
| -rw-r--r-- | src/games/tetris/Board.cpp | 8 | ||||
| -rw-r--r-- | src/games/tetris/Board.hpp | 3 | ||||
| -rw-r--r-- | src/games/tetris/Tetris.cpp | 45 | ||||
| -rw-r--r-- | src/games/tetris/Tetris.hpp | 4 | ||||
| -rw-r--r-- | src/games/tetris/Tetromino.cpp | 12 | ||||
| -rw-r--r-- | src/games/tetris/Tetromino.hpp | 5 | ||||
| -rw-r--r-- | src/main.cpp | 35 | ||||
| -rw-r--r-- | src/renderer/RSoftwareBackend.cpp | 210 | ||||
| -rw-r--r-- | src/renderer/RSoftwareBackend.hpp | 41 | ||||
| -rw-r--r-- | src/renderer/RenderGroup.cpp | 106 | ||||
| -rw-r--r-- | src/renderer/RenderGroup.hpp | 84 | ||||
| -rw-r--r-- | src/renderer/Renderer.cpp | 311 | ||||
| -rw-r--r-- | src/renderer/Renderer.hpp | 110 |
22 files changed, 576 insertions, 549 deletions
diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt index 8591621..dae369c 100644 --- a/cmake/CMakeLists.txt +++ b/cmake/CMakeLists.txt @@ -29,7 +29,7 @@ target_sources(fsarcade PUBLIC ${FSARCADE_SRC_DIR}/basic/math.cpp ${FSARCADE_SRC_DIR}/common/Font.cpp ${FSARCADE_SRC_DIR}/renderer/Renderer.cpp - ${FSARCADE_SRC_DIR}/renderer/RenderGroup.cpp + ${FSARCADE_SRC_DIR}/renderer/RSoftwareBackend.cpp ${FSARCADE_SRC_DIR}/games/Game.cpp ${FSARCADE_SRC_DIR}/games/tetris/Tetris.cpp ${FSARCADE_SRC_DIR}/games/tetris/Tetromino.cpp diff --git a/src/basic/math.cpp b/src/basic/math.cpp index 3067255..81fcbbf 100644 --- a/src/basic/math.cpp +++ b/src/basic/math.cpp @@ -1,50 +1,45 @@ #include <basic/math.hpp> +/* V2ST */ -V2ST::V2ST(size_t x, size_t y) : x(x), y(y) { -} - -V2ST::V2ST(int32_t x, int32_t y) : x((size_t)x), y((size_t)y) { -} - -bool V2ST::operator==(V2ST &b) { +bool +V2ST::operator==(V2ST& b) +{ bool result = this->x == b.x && this->y == b.y; return result; } -V2F32::V2F32(float x, float y) { - this->x = x; - this->y = y; -} -V2F32 V2F32::operator/(float scalar) { - V2F32 result = {}; +/* V2F32 */ + +V2F32 +V2F32::operator/(float scalar) +{ + V2F32 result {}; result.x = this->x / scalar; result.y = this->y / scalar; return result; } -V2F32 V2F32::operator*(float scalar) { - V2F32 result = {}; +V2F32 +V2F32::operator*(float scalar) +{ + V2F32 result {}; result.x = this->x * scalar; result.y = this->y * scalar; return result; } V2F32 V2F32::operator+(V2F32 other) { - V2F32 result = {}; + V2F32 result {}; result.x = this->x + other.x; result.y = this->y + other.y; return result; } -V3F32::V3F32(float x, float y, float z) { - this->x = x; - this->y = y; - this->z = z; -} +/* V3F32 */ V3F32 V3F32::operator/(float scalar) { V3F32 result = {}; @@ -63,15 +58,12 @@ V3F32 V3F32::operator*(float scalar) { } -V4F32::V4F32(float x, float y, float z, float w) { - this->x = x; - this->y = y; - this->z = z; - this->w = w; -} +/* V4F32 */ -V4F32 V4F32::operator/(float scalar) { - V4F32 result = {}; +V4F32 +V4F32::operator/(float scalar) +{ + V4F32 result {}; result.x = this->x / scalar; result.y = this->y / scalar; result.z = this->z / scalar; @@ -79,8 +71,10 @@ V4F32 V4F32::operator/(float scalar) { return result; } -V4F32 V4F32::operator*(float scalar) { - V4F32 result = {}; +V4F32 +V4F32::operator*(float scalar) +{ + V4F32 result {}; result.x = this->x * scalar; result.y = this->y * scalar; result.z = this->z * scalar; @@ -88,10 +82,12 @@ V4F32 V4F32::operator*(float scalar) { return result; } -V2I32::V2I32(int32_t x, int32_t y) : x(x), y(y) { -} -bool V2I32::operator==(V2I32 other) { +/* V2I32 */ + +bool +V2I32::operator==(V2I32 other) +{ bool result = x == other.x && y == other.y; return result; } diff --git a/src/basic/math.hpp b/src/basic/math.hpp index 40be80a..c5b5940 100644 --- a/src/basic/math.hpp +++ b/src/basic/math.hpp @@ -6,9 +6,6 @@ struct V2ST { size_t x; size_t y; - V2ST() = default; - V2ST(size_t x, size_t y); - V2ST(int32_t x, int32_t y); bool operator==(V2ST &b); bool operator==(const V2ST& other) const { return x == other.x && y == other.y; @@ -19,8 +16,6 @@ struct V2F32 { float x; float y; - V2F32() = default; - V2F32(float x, float y); V2F32 operator/(float scalar); V2F32 operator*(float scalar); V2F32 operator+(V2F32 other); @@ -31,8 +26,6 @@ struct V3F32 { float y; float z; - V3F32() = default; - V3F32(float x, float y, float z); V3F32 operator/(float scalar); V3F32 operator*(float scalar); }; @@ -43,8 +36,6 @@ struct V4F32 { float z; float w; - V4F32() = default; - V4F32 (float x, float y, float z, float w); V4F32 operator/(float scalar); V4F32 operator*(float scalar); }; @@ -53,8 +44,6 @@ struct V2I32 { int32_t x; int32_t y; - V2I32() = default; - V2I32 (int32_t x, int32_t y); bool operator==(V2I32 other); }; diff --git a/src/games/Game.cpp b/src/games/Game.cpp index 4f7b7a6..5619c8a 100644 --- a/src/games/Game.cpp +++ b/src/games/Game.cpp @@ -9,7 +9,7 @@ std::unique_ptr<Game> -Game::Select(GameType type, RenderGroup& render_group) +Game::Select(GameType type) { switch (type) { case NO_GAME: { @@ -17,15 +17,15 @@ Game::Select(GameType type, RenderGroup& render_group) } break; case TETRIS: { - return std::make_unique<Tetris>(render_group); + return std::make_unique<Tetris>(); } break; case SNAKE: { - return std::make_unique<Snake>(render_group); + return std::make_unique<Snake>(); } break; case MINESWEEPER: { - return std::make_unique<Minesweeper>(render_group); + return std::make_unique<Minesweeper>(); } break; InvalidDefaultCase; diff --git a/src/games/Game.hpp b/src/games/Game.hpp index b09b618..2156b7e 100644 --- a/src/games/Game.hpp +++ b/src/games/Game.hpp @@ -4,7 +4,6 @@ #include <SDL3/SDL.h> #include <memory> #include <vector> -#include <renderer/RenderGroup.hpp> struct SDL_Window; @@ -17,7 +16,7 @@ public: MINESWEEPER }; - static std::unique_ptr<Game> Select(GameType type, RenderGroup& render_group); + static std::unique_ptr<Game> Select(GameType type); Game() = default; virtual ~Game(); diff --git a/src/games/minesweeper/Minesweeper.cpp b/src/games/minesweeper/Minesweeper.cpp index 2174ab8..8e27eb3 100644 --- a/src/games/minesweeper/Minesweeper.cpp +++ b/src/games/minesweeper/Minesweeper.cpp @@ -1,5 +1,7 @@ -#include <renderer/RenderGroup.hpp> #include <games/minesweeper/Minesweeper.hpp> +#include <renderer/Renderer.hpp> + +#include <imgui.h> #include <algorithm> #include <random> @@ -7,8 +9,7 @@ // Todo: winning condition (maybe: total_cells - uncovered_cells = mine_count) -Minesweeper::Minesweeper(RenderGroup& render_group) - : m_render_group {render_group} +Minesweeper::Minesweeper() { m_font.Init("fonts/dejavu_ttf/DejaVuSansMono.ttf", 16); for (uint32_t i = 0; i < m_digit_glyphs.size(); ++i) { @@ -112,9 +113,8 @@ Minesweeper::InitAdjacentMineCounters() bool Minesweeper::Update(std::vector<SDL_Event>& events) { - Color clear_color = {0.3f, 0.2f, 0.3f}; - m_render_group.SetCameraSize(4.0f, 3.0f); - m_render_group.Clear(clear_color); + g_renderer.SetCameraSize(4.0f, 3.0f); + g_renderer.Clear({0.3f, 0.2f, 0.3f}); for (SDL_Event &event : events) { if (m_run_state == exit) { @@ -171,8 +171,8 @@ Minesweeper::ProcessEventDuringResume(SDL_Event &event) } break; case SDL_EVENT_MOUSE_BUTTON_DOWN: { - V2F32 click_screen_pos = {event.button.x, (float)m_render_group.m_ScreenHeight -1 - event.button.y}; - V2F32 click_view_pos = ScreenPosToViewPos(click_screen_pos, m_render_group); + V2F32 click_screen_pos = {event.button.x, (float)g_renderer.m_screen_h-1 - event.button.y}; + V2F32 click_view_pos = ScreenPosToViewPos(click_screen_pos); float x_adjusted = click_view_pos.x - m_grid_pos.x; float y_adjusted = click_view_pos.y - m_grid_pos.y; @@ -285,12 +285,12 @@ Minesweeper::IsMine(int32_t x, int32_t y) } V2F32 -Minesweeper::ScreenPosToViewPos(V2F32 screen_pos, RenderGroup &render_group) +Minesweeper::ScreenPosToViewPos(V2F32 screen_pos) { // e.g. [0, 1024] -> [0, 1] -> [0, 4] // e.g. [0, 768] -> [0, 1] -> [0, 3] - float screen_width = (float)render_group.m_ScreenWidth; - float screen_height = (float)render_group.m_ScreenHeight; + float screen_width = (float)g_renderer.m_screen_w; + float screen_height = (float)g_renderer.m_screen_h; V2F32 view_pos; view_pos.x = (screen_pos.x / screen_width) * m_world_width; @@ -381,7 +381,7 @@ Minesweeper::DrawBoard() bool is_mine = IsMine(x, y); if (is_covered) { - m_render_group.PushRectangle(cell_rect, 0.0f, covered_cell_color); + g_renderer.PushRectangle(cell_rect, 0.0f, covered_cell_color); if (is_flagged) { @@ -396,7 +396,7 @@ Minesweeper::DrawBoard() flag_pos.x + flag_size.x, flag_pos.y + flag_size.y, }; - m_render_group.PushRectangle(flag_rect, flag_pos.z, flag_color); + g_renderer.PushRectangle(flag_rect, flag_pos.z, flag_color); } } else { @@ -412,10 +412,10 @@ Minesweeper::DrawBoard() mine_pos.x + m_cell_inner_size.x, mine_pos.y + m_cell_inner_size.y, }; - m_render_group.PushRectangle(mine_rect, mine_pos.z, mine_color); + g_renderer.PushRectangle(mine_rect, mine_pos.z, mine_color); } else { - m_render_group.PushRectangle(cell_rect, 0.0f, uncovered_cell_color); + g_renderer.PushRectangle(cell_rect, 0.0f, uncovered_cell_color); // Todo: Figure out how to scale this properly. // 256.0f is a random number that just works for now. @@ -425,7 +425,7 @@ Minesweeper::DrawBoard() 256.0f }; size_t mine_count_val = (size_t)m_adjacent_mine_counts[y*m_grid_width + x]; - m_render_group.PushBitmap( + g_renderer.PushMonoBitmap( mine_count_pos, m_digit_glyphs[mine_count_val].bitmap.width, m_digit_glyphs[mine_count_val].bitmap.height, diff --git a/src/games/minesweeper/Minesweeper.hpp b/src/games/minesweeper/Minesweeper.hpp index b89743e..b90f4a3 100644 --- a/src/games/minesweeper/Minesweeper.hpp +++ b/src/games/minesweeper/Minesweeper.hpp @@ -1,8 +1,7 @@ #pragma once -#include "imgui.h" -#include "renderer/RenderGroup.hpp" #include <games/Game.hpp> +#include <basic/math.hpp> #include <common/Font.hpp> #include <array> @@ -38,7 +37,7 @@ public: }; public: - Minesweeper(RenderGroup& render_group); + Minesweeper(); ~Minesweeper() = default; bool Update(std::vector<SDL_Event>& events) override; @@ -61,7 +60,7 @@ private: bool IsMine(int32_t x, int32_t y); - V2F32 ScreenPosToViewPos(V2F32 screen_pos, RenderGroup& render_group); + V2F32 ScreenPosToViewPos(V2F32 screen_pos); private: @@ -78,8 +77,6 @@ private: private: - RenderGroup& m_render_group; - RunState m_run_state = start_menu; Difficulty m_difficulty = beginner; diff --git a/src/games/snake/Snake.cpp b/src/games/snake/Snake.cpp index aa8cedf..a5fd76c 100644 --- a/src/games/snake/Snake.cpp +++ b/src/games/snake/Snake.cpp @@ -1,9 +1,9 @@ +#include "renderer/Renderer.hpp" #include <games/snake/Snake.hpp> #include <imgui.h> -Snake::Snake(RenderGroup& render_group) - : m_RenderGroup{render_group} +Snake::Snake() { m_IsPaused = false; m_IsRunning = true; @@ -46,8 +46,8 @@ bool Snake::Update(std::vector<SDL_Event> &events) { Color clear_color = {0.3f, 0.3f, 0.3f, 1.0f}; - m_RenderGroup.SetCameraSize(4.0f, 3.0f); - m_RenderGroup.Clear(clear_color); + g_renderer.SetCameraSize(4.0f, 3.0f); + g_renderer.Clear(clear_color); for (SDL_Event &event : events) { @@ -277,7 +277,7 @@ void Snake::Draw() { map_world_pos.y + map_world_dim.y }; Color bg_color = {0.0f, 0.0f, 0.0f, 1.0f}; - m_RenderGroup.PushRectangle(map_world_rect, map_world_pos.z, bg_color); + g_renderer.PushRectangle(map_world_rect, map_world_pos.z, bg_color); /* draw snake */ @@ -306,7 +306,7 @@ void Snake::Draw() { }; Color color = {0.3f, 0.3f, 0.3f, 1.0f}; - m_RenderGroup.PushRectangle(world_rect, world_pos.z, color); + g_renderer.PushRectangle(world_rect, world_pos.z, color); tail++; } tail = 0; @@ -334,7 +334,7 @@ void Snake::Draw() { }; Color color = {0.3f, 0.3f, 0.3f, 1.0f}; - m_RenderGroup.PushRectangle(world_rect, world_pos.z, color); + g_renderer.PushRectangle(world_rect, world_pos.z, color); tail++; } @@ -353,7 +353,7 @@ void Snake::Draw() { pos.y + dim.y }; Color color = {0.3f, 0.6f, 0.4f, 1.0f}; - m_RenderGroup.PushRectangle(rect, pos.z, color); + g_renderer.PushRectangle(rect, pos.z, color); } void Snake::DoImgui() { diff --git a/src/games/snake/Snake.hpp b/src/games/snake/Snake.hpp index ef1fd34..1223cbe 100644 --- a/src/games/snake/Snake.hpp +++ b/src/games/snake/Snake.hpp @@ -1,7 +1,7 @@ #pragma once -#include <renderer/RenderGroup.hpp> #include <games/Game.hpp> +#include <basic/math.hpp> #include <random> @@ -17,7 +17,7 @@ public: public: - Snake(RenderGroup& render_group); + Snake(); bool Update(std::vector<SDL_Event> &events) override; @@ -37,8 +37,6 @@ private: static constexpr int32_t MAX_MAP_WIDTH = 16; static constexpr int32_t MAX_MAP_HEIGHT = 16; - RenderGroup& m_RenderGroup; - bool m_IsPaused; bool m_IsRunning; diff --git a/src/games/tetris/Board.cpp b/src/games/tetris/Board.cpp index ab70cd5..dd83cb1 100644 --- a/src/games/tetris/Board.cpp +++ b/src/games/tetris/Board.cpp @@ -1,6 +1,6 @@ -#include <renderer/RenderGroup.hpp> #include <games/tetris/Tetromino.hpp> #include <games/tetris/Board.hpp> +#include <renderer/Renderer.hpp> Board::Board() { for (int y = 0; y < 2; y++) { @@ -78,7 +78,7 @@ int32_t Board::ClearRows(int32_t y0) { return rows_cleared; } -void Board::Draw(int32_t level, RenderGroup& render_group) { +void Board::Draw(int32_t level) { float world_width = 4.0f; float world_height = 3.0f; float tetromino_size_with_border = world_height / 20.0f; @@ -107,7 +107,7 @@ void Board::Draw(int32_t level, RenderGroup& render_group) { bg_world_pos.y + bg_world_dim.y, }; Color bg_color = {0.0f, 0.0f, 0.0f, 1.0f}; - render_group.PushRectangle(bg_world_rect, bg_world_pos.z, bg_color); + g_renderer.PushRectangle(bg_world_rect, bg_world_pos.z, bg_color); // tetromino parts @@ -137,7 +137,7 @@ void Board::Draw(int32_t level, RenderGroup& render_group) { Color color = Tetromino::GetColor(tetromino_id); - render_group.PushRectangle(world_rect, world_pos.z, color); + g_renderer.PushRectangle(world_rect, world_pos.z, color); } } } diff --git a/src/games/tetris/Board.hpp b/src/games/tetris/Board.hpp index 0019a7f..f841e2f 100644 --- a/src/games/tetris/Board.hpp +++ b/src/games/tetris/Board.hpp @@ -1,7 +1,6 @@ #pragma once #include <basic/defs.hpp> -#include <renderer/RenderGroup.hpp> class Tetromino; @@ -15,7 +14,7 @@ public: Board(); int32_t PlaceTetromino(Tetromino &tetromino); - void Draw(int32_t level, RenderGroup& render_group); + void Draw(int32_t level); private: diff --git a/src/games/tetris/Tetris.cpp b/src/games/tetris/Tetris.cpp index 331e8a6..a8004f3 100644 --- a/src/games/tetris/Tetris.cpp +++ b/src/games/tetris/Tetris.cpp @@ -1,7 +1,9 @@ #include <games/tetris/Tetromino.hpp> +#include <games/tetris/Tetris.hpp> +#include <renderer/Renderer.hpp> + #include <SDL3/SDL_events.h> #include <SDL3/SDL_timer.h> -#include <games/tetris/Tetris.hpp> #include <imgui.h> #include <fstream> @@ -9,8 +11,7 @@ // Todo: change to new font scaling api in imgui first // Todo: test text with hardcoded gap + dummy to ensure it gets placed as expected -Tetris::Tetris(RenderGroup& m_RenderGroup) : - m_RenderGroup(m_RenderGroup), +Tetris::Tetris() : m_ActiveTetromino(m_Board), m_NextTetromino(m_Board) { @@ -37,8 +38,8 @@ void Tetris::Restart() { bool Tetris::Update(std::vector<SDL_Event> &events) { Color clear_color = {0.2f, 0.2f, 0.2f, 1.0f}; - m_RenderGroup.SetCameraSize(4.0f, 3.0f); - m_RenderGroup.Clear(clear_color); + g_renderer.SetCameraSize(4.0f, 3.0f); + g_renderer.Clear(clear_color); if (m_RunningState == TetrisRunningState::Restart) { Restart(); @@ -213,8 +214,8 @@ void Tetris::HandleGameOver() { } void Tetris::Draw() { - m_Board.Draw(m_Level, m_RenderGroup); - m_ActiveTetromino.Draw(m_RenderGroup); + m_Board.Draw(m_Level); + m_ActiveTetromino.Draw(); DrawNextTetromino(); DrawStatistics(); @@ -260,7 +261,7 @@ void Tetris::DrawGameOverMenu() { void Tetris::DrawLineCounter() { V2F32 view_pos = {0.5f, 2.6f}; - ImVec2 screen_pos = m_RenderGroup.ViewPosToScreenPosImGui(view_pos); + ImVec2 screen_pos = g_renderer.ViewPosToScreenPosImGui(view_pos); ImGui::SetNextWindowPos(screen_pos); ImGui::Begin("TetrisLines", nullptr, s_DefaultImGuiWindowFlags); @@ -273,33 +274,33 @@ void Tetris::DrawStatistics() { V2F32 view_advance = {0.0f, 0.2f}; V2F32 view_text_title_pos = view_tetrominoes_pos + V2F32(0.02f, 0.4f); - ImVec2 screen_text_title_pos = m_RenderGroup.ViewPosToScreenPosImGui(view_text_title_pos); + 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 = m_RenderGroup.ViewPosToScreenPosImGui(view_text_pos); - ImVec2 screen_text_gap = m_RenderGroup.ViewSizeToScreenSizeImGui(view_text_gap); + ImVec2 screen_text_pos = g_renderer.ViewPosToScreenPosImGui(view_text_pos); + ImVec2 screen_text_gap = g_renderer.ViewSizeToScreenSizeImGui(view_text_gap); - Tetromino::Draw(Tetromino::t_piece, 0, view_tetrominoes_pos, 0.5f, m_RenderGroup); + Tetromino::Draw(Tetromino::t_piece, 0, view_tetrominoes_pos, 0.5f); view_tetrominoes_pos.y -= view_advance.y; - Tetromino::Draw(Tetromino::j_piece, 0, view_tetrominoes_pos, 0.5f, m_RenderGroup); + Tetromino::Draw(Tetromino::j_piece, 0, view_tetrominoes_pos, 0.5f); view_tetrominoes_pos.y -= view_advance.y; - Tetromino::Draw(Tetromino::z_piece, 0, view_tetrominoes_pos, 0.5f, m_RenderGroup); + Tetromino::Draw(Tetromino::z_piece, 0, view_tetrominoes_pos, 0.5f); view_tetrominoes_pos.y -= view_advance.y; - Tetromino::Draw(Tetromino::o_piece, 0, view_tetrominoes_pos, 0.5f, m_RenderGroup); + Tetromino::Draw(Tetromino::o_piece, 0, view_tetrominoes_pos, 0.5f); view_tetrominoes_pos.y -= view_advance.y; - Tetromino::Draw(Tetromino::s_piece, 0, view_tetrominoes_pos, 0.5f, m_RenderGroup); + Tetromino::Draw(Tetromino::s_piece, 0, view_tetrominoes_pos, 0.5f); view_tetrominoes_pos.y -= view_advance.y; - Tetromino::Draw(Tetromino::l_piece, 0, view_tetrominoes_pos, 0.5f, m_RenderGroup); + Tetromino::Draw(Tetromino::l_piece, 0, view_tetrominoes_pos, 0.5f); view_tetrominoes_pos.y -= view_advance.y; - Tetromino::Draw(Tetromino::i_piece, 0, view_tetrominoes_pos, 0.5f, m_RenderGroup); + Tetromino::Draw(Tetromino::i_piece, 0, view_tetrominoes_pos, 0.5f); view_tetrominoes_pos.y -= view_advance.y; @@ -332,7 +333,7 @@ void Tetris::DrawStatistics() { void Tetris::DrawScore() { V2F32 view_pos = {3.0f, 2.2f}; - ImVec2 screen_pos = m_RenderGroup.ViewPosToScreenPosImGui(view_pos); + ImVec2 screen_pos = g_renderer.ViewPosToScreenPosImGui(view_pos); ImGui::SetNextWindowPos(screen_pos); ImGui::Begin("TetrisScore", nullptr, s_DefaultImGuiWindowFlags); @@ -343,7 +344,7 @@ void Tetris::DrawScore() { void Tetris::DrawNextTetromino() { V2F32 text_view_pos = {3.0f, 1.8f}; - ImVec2 text_screen_pos = m_RenderGroup.ViewPosToScreenPosImGui(text_view_pos); + ImVec2 text_screen_pos = g_renderer.ViewPosToScreenPosImGui(text_view_pos); ImGui::SetNextWindowPos(text_screen_pos); ImGui::Begin("TetrisNextTetromino", nullptr, s_DefaultImGuiWindowFlags); @@ -352,12 +353,12 @@ void Tetris::DrawNextTetromino() { V2F32 tetromino_view_pos = {3.0, 1.4f}; - Tetromino::Draw(m_NextTetromino.GetId(), 0, tetromino_view_pos, 0.5f, m_RenderGroup); + Tetromino::Draw(m_NextTetromino.GetId(), 0, tetromino_view_pos, 0.5f); } void Tetris::DrawLevel() { V2F32 view_pos = {3.0f, 1.2f}; - ImVec2 screen_pos = m_RenderGroup.ViewPosToScreenPosImGui(view_pos); + ImVec2 screen_pos = g_renderer.ViewPosToScreenPosImGui(view_pos); ImGui::SetNextWindowPos(screen_pos); ImGui::Begin("TetrisLevel", nullptr, s_DefaultImGuiWindowFlags); diff --git a/src/games/tetris/Tetris.hpp b/src/games/tetris/Tetris.hpp index f4a8caf..828f2e5 100644 --- a/src/games/tetris/Tetris.hpp +++ b/src/games/tetris/Tetris.hpp @@ -4,7 +4,6 @@ #include <games/Game.hpp> #include <games/tetris/Tetromino.hpp> #include <games/tetris/Board.hpp> -#include <renderer/RenderGroup.hpp> enum class TetrisRunningState { Resume, @@ -17,7 +16,7 @@ enum class TetrisRunningState { class Tetris : public Game { public: - Tetris(RenderGroup& render_group); + Tetris(); bool Update(std::vector<SDL_Event> &events) override; void HandleTetrominoPlacement(); @@ -45,7 +44,6 @@ private: private: - RenderGroup& m_RenderGroup; TetrisRunningState m_RunningState = TetrisRunningState::Resume; float m_DtInSecondsRemaining = 0.0f; diff --git a/src/games/tetris/Tetromino.cpp b/src/games/tetris/Tetromino.cpp index a77fa57..94343c0 100644 --- a/src/games/tetris/Tetromino.cpp +++ b/src/games/tetris/Tetromino.cpp @@ -1,6 +1,8 @@ #include <games/tetris/Tetromino.hpp> +#include <renderer/Renderer.hpp> + #include <random> -#include <stdlib.h> +#include <cstdlib> // layout of a left_aligned_bitmap: xxxx000000000000 // layout of a board_bitmap is 111xxxxxxxxxx111 @@ -119,7 +121,7 @@ bool Tetromino::MaybeMoveDown() { return false; } -void Tetromino::Draw(RenderGroup &render_group) const { +void Tetromino::Draw() const { float world_width = 4.0f; float world_height = 3.0f; float tetromino_size_with_border = world_height / 20.0f; @@ -132,7 +134,7 @@ void Tetromino::Draw(RenderGroup &render_group) const { y0 * tetromino_size_with_border }; - Tetromino::Draw(m_Id, m_Ori, world_pos, 1.0f, render_group); + Tetromino::Draw(m_Id, m_Ori, world_pos, 1.0f); } bool Tetromino::IsCollisionWithBoard(TetrominoId id, BoardPos pos, int32_t ori, uint16_t *board_bitmap) { @@ -175,7 +177,7 @@ Color Tetromino::GetColor(TetrominoId id) { return color; } -void Tetromino::Draw(TetrominoId id, int32_t ori, V2F32 pos, float scale, RenderGroup &render_group) { +void Tetromino::Draw(TetrominoId id, int32_t ori, V2F32 pos, float scale) { int32_t id_ = static_cast<int32_t>(id); float world_height = 3.0f; @@ -209,7 +211,7 @@ void Tetromino::Draw(TetrominoId id, int32_t ori, V2F32 pos, float scale, Render Color color = GetColor(id); - render_group.PushRectangle(world_rect, world_pos.z, color); + g_renderer.PushRectangle(world_rect, world_pos.z, color); } } } diff --git a/src/games/tetris/Tetromino.hpp b/src/games/tetris/Tetromino.hpp index 0f97821..ecc986b 100644 --- a/src/games/tetris/Tetromino.hpp +++ b/src/games/tetris/Tetromino.hpp @@ -2,7 +2,6 @@ #include <basic/defs.hpp> #include <basic/math.hpp> -#include <renderer/RenderGroup.hpp> #include <games/tetris/Board.hpp> @@ -46,14 +45,14 @@ public: void MaybeMoveHorizontally(TetrominoDirection direction); void MaybeRotate(TetrominoRotation rotation); - void Draw(RenderGroup &render_group) const; + void Draw() const; public: static bool IsCollisionWithBoard(TetrominoId id, BoardPos pos, int32_t ori, uint16_t *board_bitmap); static void GetBitmap(TetrominoId id, BoardPos pos, int32_t ori, uint16_t *bitmap); static Color GetColor(TetrominoId id); - static void Draw(TetrominoId id, int32_t ori, V2F32 pos, float scale, RenderGroup &render_group); + static void Draw(TetrominoId id, int32_t ori, V2F32 pos, float scale); private: diff --git a/src/main.cpp b/src/main.cpp index 91192b4..bdd5a18 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,7 +1,5 @@ #include <basic/defs.hpp> -#include <cstdlib> #include <memory> -#include <renderer/RenderGroup.hpp> #include <renderer/Renderer.hpp> #include <games/Game.hpp> @@ -14,13 +12,13 @@ #include <imgui_impl_opengl3.h> #include <imgui_impl_sdl3.h> -#include <stdlib.h> -#include <assert.h> #include <iostream> +#include <cstdlib> +#include <assert.h> Game::GameType -do_menu(RenderGroup &render_group) +do_menu() { Game::GameType type = Game::NO_GAME; @@ -41,8 +39,8 @@ do_menu(RenderGroup &render_group) Color clear_color = {0.4f, 0.4f, 0.4f, 1.0f}; - render_group.SetCameraSize(4.0f, 3.0f); - render_group.Clear(clear_color); + g_renderer.SetCameraSize(4.0f, 3.0f); + g_renderer.Clear(clear_color); return type; @@ -105,18 +103,15 @@ main(int argc, char **argv) ImGui_ImplOpenGL3_Init(glsl_version); - std::unique_ptr<Game> game = nullptr; - Renderer renderer {window}; - if (!renderer.Init()) { - return EXIT_FAILURE; - } + g_renderer.Init(window); - RenderGroup render_group; - SDL_GetWindowSize(window, &render_group.m_ScreenWidth, &render_group.m_ScreenHeight); + + std::unique_ptr<Game> game = nullptr; std::vector<SDL_Event> game_events; game_events.reserve(32); + for (;;) { ImGui_ImplOpenGL3_NewFrame(); ImGui_ImplSDL3_NewFrame(); @@ -148,9 +143,7 @@ main(int argc, char **argv) int w, h; SDL_GetWindowSize(window, &w, &h); - render_group.m_ScreenWidth = w; - render_group.m_ScreenHeight = h; - renderer.ResizeCanvas(w, h); + g_renderer.SetScreenSize(w, h); if (game) { @@ -160,9 +153,9 @@ main(int argc, char **argv) } } else { - Game::GameType type = do_menu(render_group); + Game::GameType type = do_menu(); if (type != Game::NO_GAME) { - game = Game::Select(type, render_group); + game = Game::Select(type); } } game_events.clear(); @@ -177,11 +170,11 @@ main(int argc, char **argv) ImGui::End(); - renderer.Draw(render_group); + g_renderer.Draw(); ImGui::Render(); ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); SDL_GL_SwapWindow(window); - render_group.Reset(); + g_renderer.Reset(); } QUIT: diff --git a/src/renderer/RSoftwareBackend.cpp b/src/renderer/RSoftwareBackend.cpp new file mode 100644 index 0000000..f0e2e5f --- /dev/null +++ b/src/renderer/RSoftwareBackend.cpp @@ -0,0 +1,210 @@ +#include <renderer/RSoftwareBackend.hpp> + +#include <SDL3/SDL_video.h> +#include <GL/glew.h> + +#include <algorithm> +#include <cstdlib> +#include <cstdio> + + +RSoftwareBackend::RSoftwareBackend(SDL_Window* window, Renderer& renderer) + : m_window {window} + , m_renderer {renderer} +{ + m_canvas.rshift = 0; + m_canvas.gshift = 8; + m_canvas.bshift = 16; + m_canvas.ashift = 24; + m_canvas.pixels = nullptr; + + + glEnable(GL_TEXTURE_2D); + glActiveTexture(GL_TEXTURE0); + + glGenTextures(1, &m_gltexture_id); + glBindTexture(GL_TEXTURE_2D, m_gltexture_id); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); +} + +void +RSoftwareBackend::ResizeCanvas(int32_t w, int32_t h) +{ + size_t realloc_size = (size_t)(w * h) * sizeof(m_canvas.pixels[0]); + void *realloc_data = realloc(m_canvas.pixels, realloc_size); + if (!realloc_data) { + printf("could not resize offscreen buffer\n"); + return; + } + + m_canvas.w = w; + m_canvas.h = h; + m_canvas.pixels = (uint32_t*)realloc_data; +} + +void +RSoftwareBackend::Draw() +{ + ResizeCanvas(m_renderer.m_screen_w, m_renderer.m_screen_h); + 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: { + DrawRectangle(entity.rect); + } break; + + case REntityType_MonoBitmap: { + Color color = {0.0f, 0.0f, 0.0f, 1.0f}; + DrawMonoBitmap(entity.bitmap, color); + } break; + + default:; + } + } + + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, m_gltexture_id); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_canvas.w, m_canvas.h, 0, GL_RGBA, GL_UNSIGNED_BYTE, m_canvas.pixels); + + glBegin(GL_QUADS); + glTexCoord2f(0.0f, 0.0f); glVertex2f(-1.0f, -1.0f); + glTexCoord2f(1.0f, 0.0f); glVertex2f( 1.0f, -1.0f); + glTexCoord2f(1.0f, 1.0f); glVertex2f( 1.0f, 1.0f); + glTexCoord2f(0.0f, 1.0f); glVertex2f(-1.0f, 1.0f); + glEnd(); + + glBindTexture(GL_TEXTURE_2D, 0); +} + +void +RSoftwareBackend::SortRenderEntities() +{ + auto& sort_entries = m_renderer.m_sort_entries; + std::sort(sort_entries.begin(), sort_entries.end(), + [](const RSortEntry& e1, const RSortEntry& e2) { + return e1.z < e2.z; + }); +} + +void +RSoftwareBackend::DrawRectangle(REntity_Rectangle& rect) +{ + int32_t xmin = m_renderer.WorldXToScreenX(rect.x0); + int32_t ymin = m_renderer.WorldYToScreenY(rect.y0); + int32_t xmax = m_renderer.WorldXToScreenX(rect.x1); + int32_t ymax = m_renderer.WorldYToScreenY(rect.y1); + + if (xmin < 0) { + xmin = 0; + } + if (ymin < 0) { + ymin = 0; + } + if (xmax >= m_canvas.w) { + xmax = m_canvas.w - 1; + } + if (ymax >= m_canvas.h) { + ymax = m_canvas.h - 1; + } + + uint32_t rshift = m_canvas.rshift; + uint32_t gshift = m_canvas.gshift; + uint32_t bshift = m_canvas.bshift; + for (int32_t y = ymin; y <= ymax; ++y) { + uint32_t *pixel = m_canvas.pixels + y * m_canvas.w + xmin; + for (int32_t x = xmin; x <= xmax; ++x) { + uint32_t r = (uint32_t)(rect.color.r * 255.0f); + uint32_t g = (uint32_t)(rect.color.g * 255.0f); + uint32_t b = (uint32_t)(rect.color.b * 255.0f); + uint32_t val = r << rshift | g << gshift | b << bshift; + *pixel++ = val; + } + } +} + + +void +RSoftwareBackend::DrawMonoBitmap(REntity_MonoBitmap& mono_bitmap, Color color) +{ + int32_t x0 = (int32_t)mono_bitmap.x; + int32_t y0 = (int32_t)mono_bitmap.y; + int32_t x1 = (int32_t)mono_bitmap.x + mono_bitmap.w - 1; + int32_t y1 = (int32_t)mono_bitmap.y + mono_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; + } + + + uint32_t rshift = m_canvas.rshift; + uint32_t gshift = m_canvas.gshift; + uint32_t bshift = m_canvas.bshift; + + uint8_t* grayscale = (uint8_t*)mono_bitmap.data + (-cut_bot * mono_bitmap.w) + (-cut_left); + uint32_t* rgba = m_canvas.pixels + y0 * m_canvas.w + x0; + for (int32_t y = y0; y <= y1; y++) { + uint8_t *grayscale_pixels = grayscale; + uint32_t *rgba_pixels = rgba; + for (int32_t x = x0; x <= x1; x++) { + float alpha = *grayscale_pixels / 255.0f; + + // Todo: we do not want to blend with existing color! + + uint32_t rgba_result = *rgba_pixels; + float r0 = (rgba_result >> rshift) & 0xff; + float g0 = (rgba_result >> gshift) & 0xff; + float b0 = (rgba_result >> bshift) & 0xff; + + float r1 = r0 + (color.r - r0)*alpha; + float g1 = g0 + (color.g - g0)*alpha; + float b1 = b0 + (color.b - b0)*alpha; + + rgba_result = (uint32_t)r1 << rshift | (uint32_t)g1 << gshift | (uint32_t)b1 << bshift; + *rgba_pixels = rgba_result; + + grayscale_pixels++; + rgba_pixels++; + } + + grayscale += mono_bitmap.w; + rgba += m_canvas.w; + } +} + diff --git a/src/renderer/RSoftwareBackend.hpp b/src/renderer/RSoftwareBackend.hpp new file mode 100644 index 0000000..5457c7e --- /dev/null +++ b/src/renderer/RSoftwareBackend.hpp @@ -0,0 +1,41 @@ +#pragma once + +#include <SDL3/SDL.h> +#include <renderer/Renderer.hpp> + + +class RSoftwareBackend { +public: + struct Canvas { + uint32_t rshift; + uint32_t gshift; + uint32_t bshift; + uint32_t ashift; + int32_t w; + int32_t h; + uint32_t *pixels; + }; + + +public: + RSoftwareBackend(SDL_Window* window, Renderer& renderer); + + void Draw(); + + +private: + void ResizeCanvas(int32_t w, int32_t h); + void SortRenderEntities(); + + void DrawRectangle(REntity_Rectangle& rect); + void DrawMonoBitmap(REntity_MonoBitmap& mono_bitmap, Color color); + + +private: + SDL_Window* m_window{}; + Renderer& m_renderer; + + uint32_t m_gltexture_id{}; + Canvas m_canvas; +}; + diff --git a/src/renderer/RenderGroup.cpp b/src/renderer/RenderGroup.cpp deleted file mode 100644 index e393393..0000000 --- a/src/renderer/RenderGroup.cpp +++ /dev/null @@ -1,106 +0,0 @@ -#include <renderer/RenderGroup.hpp> -#include <algorithm> - -RSortEntry::RSortEntry(float z, size_t entity_index) - : z(z), entity_index(entity_index) { -} - -RenderGroup::RenderGroup() { - m_REntities.reserve(1024); - m_RSortEntries.reserve(1024); -} - -void RenderGroup::Reset() { - m_CameraWidth = 0; - m_CameraHeight = 0; - m_REntities.clear(); - m_RSortEntries.clear(); - m_REntities.reserve(1024); - m_RSortEntries.reserve(1024); -} - -void RenderGroup::SetCameraSize(float width, float height) { - m_CameraWidth = width; - m_CameraHeight = height; -} - -float RenderGroup::GetScale() { - float screen_width = static_cast<float>(m_ScreenWidth); - float screen_height = static_cast<float>(m_ScreenHeight); - float xunits = screen_width / m_CameraWidth; - float yunits = screen_height / m_CameraHeight; - float scale = std::min(xunits, yunits); - return scale; -} - -V2F32 RenderGroup::ViewPosToScreenPos(V2F32 view_pos) { - float scale = GetScale(); - float screen_width = static_cast<float>(m_ScreenWidth); - float screen_height = static_cast<float>(m_ScreenHeight); - float viewport_width = m_CameraWidth * scale; - float viewport_height = m_CameraHeight * scale; - float viewport_x0 = (screen_width - viewport_width) / 2; - float viewport_y0 = (screen_height - viewport_height) / 2; - - V2F32 result; - result.x = viewport_x0 + view_pos.x * scale; - result.y = screen_height - (viewport_y0 + view_pos.y * scale); - - return result; -} - -V2F32 RenderGroup::ViewSizeToScreenSize(V2F32 view_size) { - float scale = GetScale(); - - V2F32 result; - result.x = view_size.x * scale; - result.y = view_size.y * scale; - - return result; -} - -ImVec2 RenderGroup::ViewPosToScreenPosImGui(V2F32 view_pos) { - V2F32 screen_pos = ViewPosToScreenPos(view_pos); - ImVec2 result = {screen_pos.x, screen_pos.y}; - return result; -} - -ImVec2 RenderGroup::ViewSizeToScreenSizeImGui(V2F32 view_size) { - V2F32 screen_size = ViewSizeToScreenSize(view_size); - ImVec2 result = {screen_size.x, screen_size.y}; - return result; -} - -void RenderGroup::Clear(Color color) { - m_ClearColor = color; -} - -void RenderGroup::PushRectangle(RectF32 rect, float z, Color color) { - m_REntities.emplace_back(REntity{.rect{ - REntityType_Rectangle, - rect.x0, rect.y0, - rect.x1, rect.y1, - z, - color} - }); - m_RSortEntries.emplace_back(z, m_REntities.size()-1); -} - -void RenderGroup::PushBitmap(V3F32 pos, int w, int h, void *data) { - m_REntities.emplace_back(REntity{.bitmap{ - REntityType_Bitmap, - pos.x, pos.y, - w, h, - pos.z, - data - }}); - m_RSortEntries.emplace_back(pos.z, m_REntities.size()-1); -} - -void RenderGroup::Sort() { - std::sort(m_RSortEntries.begin(), m_RSortEntries.end(), - [](const RSortEntry& e1, const RSortEntry& e2) { - return e1.z < e2.z; - }); -} - diff --git a/src/renderer/RenderGroup.hpp b/src/renderer/RenderGroup.hpp deleted file mode 100644 index ddaa9ff..0000000 --- a/src/renderer/RenderGroup.hpp +++ /dev/null @@ -1,84 +0,0 @@ -#pragma once - -#include <basic/defs.hpp> -#include <basic/math.hpp> -#include <vector> -#include <imgui.h> - -enum REntityType : int32_t { - REntityType_Rectangle, - REntityType_Bitmap, -}; - -struct REntity_Rectangle { - REntityType type; - float x0; - float y0; - float x1; - float y1; - float z; - Color color; -}; - -struct REntity_Bitmap { - REntityType type; - float x; - float y; - int32_t w; - int32_t h; - float z; - void *data; -}; - -union REntity { - REntityType type; - REntity_Rectangle rect; - REntity_Bitmap bitmap; -}; - -struct RSortEntry { - RSortEntry(float z, size_t entity_index); - float z; - size_t entity_index; -}; - - -class RenderGroup { -public: - RenderGroup(); - void Clear(Color color); - void Reset(); - - void SetCameraSize(float width, float height); - V2F32 ViewPosToScreenPos(V2F32 view_pos); - V2F32 ViewSizeToScreenSize(V2F32 view_size); - ImVec2 ViewPosToScreenPosImGui(V2F32 view_pos); - ImVec2 ViewSizeToScreenSizeImGui(V2F32 view_size); - float GetScale(); - - -public: - void PushRectangle(RectF32 rect, float z, Color color); - void PushBitmap(V3F32 pos, int w, int h, void *bitmap); - void Sort(); - - -public: - int32_t m_ScreenWidth; - int32_t m_ScreenHeight; - - -private: - friend class Renderer; - - float m_CameraWidth; - float m_CameraHeight; - - - Color m_ClearColor; - - std::vector<REntity> m_REntities; - std::vector<RSortEntry> m_RSortEntries; -}; - - diff --git a/src/renderer/Renderer.cpp b/src/renderer/Renderer.cpp index 9217f5c..413b3cb 100644 --- a/src/renderer/Renderer.cpp +++ b/src/renderer/Renderer.cpp @@ -1,233 +1,158 @@ #include <renderer/Renderer.hpp> +#include <renderer/RSoftwareBackend.hpp> + #include <GL/glew.h> +#include <imgui.h> -#include <cstdio> -#include <cstdlib> +#include <memory> -Renderer::Renderer(SDL_Window *window) : m_Window(window), m_Canvas{} -{ -} +Renderer g_renderer; -bool -Renderer::Init() +void +Renderer::Init(SDL_Window* window) { - GLenum err = glewInit(); - if (err) { - printf("glewInit() error\n"); - return false; - } - - int32_t window_width; - int32_t window_height; - if (!SDL_GetWindowSize(m_Window, &window_width, &window_height)) { - printf("Error: SDL_GetWindowSize: %s\n", SDL_GetError()); - return false; - } - - - m_Canvas.rshift = 0; - m_Canvas.gshift = 8; - m_Canvas.bshift = 16; - m_Canvas.ashift = 24; - ResizeCanvas(window_width, window_height); + m_render_entities.reserve(1024); + m_sort_entries.reserve(1024); + m_backend = std::make_unique<RSoftwareBackend>(window, *this); +} - glEnable(GL_TEXTURE_2D); - glActiveTexture(GL_TEXTURE0); +void +Renderer::Draw() +{ + m_backend->Draw(); +} - glGenTextures(1, &m_GlTexId); - glBindTexture(GL_TEXTURE_2D, m_GlTexId); +void +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); +} - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); +void +Renderer::SetScreenSize(int32_t w, int32_t h) +{ + m_screen_w = w; + m_screen_h = h; +} +void +Renderer::SetCameraSize(float w, float h) +{ + m_camera_w = w; + m_camera_h = h; +} - return true; +int32_t +Renderer::WorldXToScreenX(float world_x) +{ + float screen_x = (world_x / m_camera_w) * (float)m_screen_w; + return (int32_t)screen_x; } +int32_t +Renderer::WorldYToScreenY(float world_y) +{ + float screen_y = (world_y / m_camera_h) * (float)m_screen_h; + return (int32_t)screen_y; +} void -Renderer::ResizeCanvas(int32_t w, int32_t h) +Renderer::Clear(Color color) { - size_t realloc_size = (size_t)(w * h) * sizeof(m_Canvas.pixels[0]); - void *realloc_data = realloc(m_Canvas.pixels, realloc_size); - if (!realloc_data) { - printf("could not resize offscreen buffer\n"); - return; - } - - m_Canvas.w = w; - m_Canvas.h = h; - m_Canvas.pixels = (uint32_t*)realloc_data; + m_clear_color = color; } - void -Renderer::Draw(RenderGroup& rgroup) +Renderer::PushRectangle(RectF32 rect, float z, Color color) { - rgroup.Sort(); - REntity_Rectangle clear_rect = { - REntityType_Rectangle, - 0, 0, - (float)(m_Canvas.w-1), (float)(m_Canvas.h-1), - 0.0f, - rgroup.m_ClearColor - }; - DrawRectangle(rgroup, clear_rect); - - - float z = -1; - for (RSortEntry sort_entry : rgroup.m_RSortEntries) { - if (sort_entry.z >= z) { - z = sort_entry.z; - } - - REntity& entity = rgroup.m_REntities[sort_entry.entity_index]; - switch (entity.type) { - case REntityType_Rectangle: { - DrawRectangle(rgroup, entity.rect); - } break; - - case REntityType_Bitmap: { - Color color = {0.0f, 0.0f, 0.0f, 1.0f}; - DrawMonoBitmap(entity.bitmap, color); - } break; - - default:; - } - } - - - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, m_GlTexId); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_Canvas.w, m_Canvas.h, 0, GL_RGBA, GL_UNSIGNED_BYTE, m_Canvas.pixels); - - glBegin(GL_QUADS); - glTexCoord2f(0.0f, 0.0f); glVertex2f(-1.0f, -1.0f); - glTexCoord2f(1.0f, 0.0f); glVertex2f( 1.0f, -1.0f); - glTexCoord2f(1.0f, 1.0f); glVertex2f( 1.0f, 1.0f); - glTexCoord2f(0.0f, 1.0f); glVertex2f(-1.0f, 1.0f); - glEnd(); - - glBindTexture(GL_TEXTURE_2D, 0); + m_render_entities.emplace_back(REntity{.rect{ + REntityType_Rectangle, + rect.x0, rect.y0, + rect.x1, rect.y1, + z, + color} + }); + m_sort_entries.emplace_back(z, m_render_entities.size()-1); } - void -Renderer::DrawRectangle(RenderGroup& rgroup, REntity_Rectangle& rect) +Renderer::PushMonoBitmap(V3F32 pos, int w, int h, void *data) { - int32_t xmin = WorldXToScreenX(rgroup, rect.x0); - int32_t ymin = WorldYToScreenY(rgroup, rect.y0); - int32_t xmax = WorldXToScreenX(rgroup, rect.x1); - int32_t ymax = WorldYToScreenY(rgroup, rect.y1); - - if (xmin < 0) { - xmin = 0; - } - if (ymin < 0) { - ymin = 0; - } - if (xmax >= m_Canvas.w) { - xmax = m_Canvas.w - 1; - } - if (ymax >= m_Canvas.h) { - ymax = m_Canvas.h - 1; - } - - uint32_t rshift = m_Canvas.rshift; - uint32_t gshift = m_Canvas.gshift; - uint32_t bshift = m_Canvas.bshift; - for (int32_t y = ymin; y <= ymax; ++y) { - uint32_t *pixel = m_Canvas.pixels + y * m_Canvas.w + xmin; - for (int32_t x = xmin; x <= xmax; ++x) { - uint32_t r = (uint32_t)(rect.color.r * 255.0f); - uint32_t g = (uint32_t)(rect.color.g * 255.0f); - uint32_t b = (uint32_t)(rect.color.b * 255.0f); - uint32_t val = r << rshift | g << gshift | b << bshift; - *pixel++ = val; - } - } + m_render_entities.emplace_back(REntity{.bitmap{ + REntityType_MonoBitmap, + pos.x, pos.y, + w, h, + pos.z, + data + }}); + m_sort_entries.emplace_back(pos.z, m_render_entities.size()-1); } -void -Renderer::DrawMonoBitmap(REntity_Bitmap& bitmap, Color color) + +/* temporary helper functions (from old RGroup api) */ + +float +Renderer::GetScale() { - int32_t x0 = (int32_t)bitmap.x; - int32_t y0 = (int32_t)bitmap.y; - int32_t x1 = (int32_t)bitmap.x + bitmap.w - 1; - int32_t y1 = (int32_t)bitmap.y + 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; - } - - - uint32_t rshift = m_Canvas.rshift; - uint32_t gshift = m_Canvas.gshift; - uint32_t bshift = m_Canvas.bshift; - - uint8_t* grayscale = (uint8_t*)bitmap.data + (-cut_bot * bitmap.w) + (-cut_left); - uint32_t* rgba = m_Canvas.pixels + y0 * m_Canvas.w + x0; - for (int32_t y = y0; y <= y1; y++) { - uint8_t *grayscale_pixels = grayscale; - uint32_t *rgba_pixels = rgba; - for (int32_t x = x0; x <= x1; x++) { - float alpha = *grayscale_pixels / 255.0f; - - // Todo: we do not want to blend with existing color! - - uint32_t rgba_result = *rgba_pixels; - float r0 = (rgba_result >> rshift) & 0xff; - float g0 = (rgba_result >> gshift) & 0xff; - float b0 = (rgba_result >> bshift) & 0xff; - - float r1 = r0 + (color.r - r0)*alpha; - float g1 = g0 + (color.g - g0)*alpha; - float b1 = b0 + (color.b - b0)*alpha; - - rgba_result = (uint32_t)r1 << rshift | (uint32_t)g1 << gshift | (uint32_t)b1 << bshift; - *rgba_pixels = rgba_result; - - grayscale_pixels++; - rgba_pixels++; - } - - grayscale += bitmap.w; - rgba += m_Canvas.w; - } + float screen_width = static_cast<float>(m_screen_w); + float screen_height = static_cast<float>(m_screen_h); + float xunits = screen_width / m_camera_w; + float yunits = screen_height / m_camera_h; + float scale = std::min(xunits, yunits); + return scale; } +V2F32 +Renderer::ViewPosToScreenPos(V2F32 view_pos) +{ + float scale = GetScale(); + float screen_width = static_cast<float>(m_screen_w); + float screen_height = static_cast<float>(m_screen_h); + float viewport_width = m_camera_w * scale; + float viewport_height = m_camera_h * scale; + float viewport_x0 = (screen_width - viewport_width) / 2; + float viewport_y0 = (screen_height - viewport_height) / 2; + + V2F32 result; + result.x = viewport_x0 + view_pos.x * scale; + result.y = screen_height - (viewport_y0 + view_pos.y * scale); + + return result; +} -int32_t -Renderer::WorldXToScreenX(RenderGroup& rgroup, float x) +V2F32 +Renderer::ViewSizeToScreenSize(V2F32 view_size) { - float xscreen = (x / rgroup.m_CameraWidth) * (float)rgroup.m_ScreenWidth; - return (int32_t)xscreen; + float scale = GetScale(); + + V2F32 result; + result.x = view_size.x * scale; + result.y = view_size.y * scale; + + return result; } -int32_t -Renderer::WorldYToScreenY(RenderGroup& rgroup, float y) +ImVec2 +Renderer::ViewPosToScreenPosImGui(V2F32 view_pos) +{ + V2F32 screen_pos = ViewPosToScreenPos(view_pos); + ImVec2 result = {screen_pos.x, screen_pos.y}; + return result; +} + +ImVec2 +Renderer::ViewSizeToScreenSizeImGui(V2F32 view_size) { - float yscreen = (y / rgroup.m_CameraHeight) * (float)rgroup.m_ScreenHeight; - return (int32_t)yscreen; + V2F32 screen_size = ViewSizeToScreenSize(view_size); + ImVec2 result = {screen_size.x, screen_size.y}; + return result; } diff --git a/src/renderer/Renderer.hpp b/src/renderer/Renderer.hpp index 24d4b71..4fdf524 100644 --- a/src/renderer/Renderer.hpp +++ b/src/renderer/Renderer.hpp @@ -1,41 +1,111 @@ #pragma once #include <basic/math.hpp> -#include <renderer/RenderGroup.hpp> + #include <SDL3/SDL.h> #include <SDL3/SDL_video.h> +#include <imgui.h> + +#include <vector> +#include <memory> + + +class RSoftwareBackend; +class Renderer; + +extern Renderer g_renderer; + + +enum REntityType : int32_t { + REntityType_Rectangle, + REntityType_MonoBitmap, +}; -struct RCanvas { - uint32_t rshift; - uint32_t gshift; - uint32_t bshift; - uint32_t ashift; +struct REntity_Rectangle { + REntityType type; + float x0; + float y0; + float x1; + float y1; + float z; + Color color; +}; + +struct REntity_MonoBitmap { + REntityType type; + float x; + float y; int32_t w; int32_t h; - uint32_t *pixels; + float z; + void *data; +}; + +union REntity { + REntityType type; + REntity_Rectangle rect; + REntity_MonoBitmap bitmap; +}; + +struct RSortEntry { + float z; + size_t entity_index; }; class Renderer { public: - Renderer(SDL_Window *window); + void Init(SDL_Window* window); + + + /* core functions */ + + void Draw(); + void Reset(); + + void Clear(Color color); + void PushRectangle(RectF32 rect, float z, Color color); + void PushMonoBitmap(V3F32 pos, int w, int h, void* bitmap); + + + /* helper functions */ + + void SetScreenSize(int32_t w, int32_t h); + void SetCameraSize(float w, float h); + + + int32_t WorldXToScreenX(float x); + int32_t WorldYToScreenY(float y); + + int32_t WorldWidthToScreenWidth(float w); + int32_t WorldHeightToScreenHeight(float h); + + + /* temporary helper functions (from old RGroup api) */ + float GetScale(); + V2F32 ViewPosToScreenPos(V2F32 view_pos); + V2F32 ViewSizeToScreenSize(V2F32 view_size); + ImVec2 ViewPosToScreenPosImGui(V2F32 view_pos); + ImVec2 ViewSizeToScreenSizeImGui(V2F32 view_size); + + + + // Todo: make this private +public: + int32_t m_screen_w; + int32_t m_screen_h; - bool Init(); - void ResizeCanvas(int32_t w, int32_t h); - void Draw(RenderGroup &render_group); + float m_camera_w; + float m_camera_h; + Color m_clear_color {}; + std::vector<REntity> m_render_entities; + std::vector<RSortEntry> m_sort_entries; -private: - void DrawRectangle(RenderGroup& rgroup, REntity_Rectangle& rect); - void DrawMonoBitmap(REntity_Bitmap& bitmap, Color color); - int32_t WorldXToScreenX(RenderGroup& rgroup, float x); - int32_t WorldYToScreenY(RenderGroup& rgroup, float y); + std::unique_ptr<RSoftwareBackend> m_backend; -private: - SDL_Window *m_Window; - uint32_t m_GlTexId; - RCanvas m_Canvas; + friend class RSoftwareBackend; }; |
