aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorfschildt <florian.schildt@protonmail.com>2025-09-29 13:20:43 +0200
committerfschildt <florian.schildt@protonmail.com>2025-09-29 13:20:43 +0200
commit9d72ed2d5801b1506158082f08bd0b47e58db17f (patch)
tree1fe30ab6dae55db5a3faaac6b8d54f67a31255d3
parentd793b79dea7d5e19982128528276cf05d6c23b5d (diff)
renderer: major refactor; vectors: now aggregates
-rw-r--r--cmake/CMakeLists.txt2
-rw-r--r--src/basic/math.cpp64
-rw-r--r--src/basic/math.hpp11
-rw-r--r--src/games/Game.cpp8
-rw-r--r--src/games/Game.hpp3
-rw-r--r--src/games/minesweeper/Minesweeper.cpp32
-rw-r--r--src/games/minesweeper/Minesweeper.hpp9
-rw-r--r--src/games/snake/Snake.cpp16
-rw-r--r--src/games/snake/Snake.hpp6
-rw-r--r--src/games/tetris/Board.cpp8
-rw-r--r--src/games/tetris/Board.hpp3
-rw-r--r--src/games/tetris/Tetris.cpp45
-rw-r--r--src/games/tetris/Tetris.hpp4
-rw-r--r--src/games/tetris/Tetromino.cpp12
-rw-r--r--src/games/tetris/Tetromino.hpp5
-rw-r--r--src/main.cpp35
-rw-r--r--src/renderer/RSoftwareBackend.cpp210
-rw-r--r--src/renderer/RSoftwareBackend.hpp41
-rw-r--r--src/renderer/RenderGroup.cpp106
-rw-r--r--src/renderer/RenderGroup.hpp84
-rw-r--r--src/renderer/Renderer.cpp311
-rw-r--r--src/renderer/Renderer.hpp110
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;
};