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