aboutsummaryrefslogtreecommitdiff
path: root/src/renderer/RSoftwareBackend.cpp
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 /src/renderer/RSoftwareBackend.cpp
parentd793b79dea7d5e19982128528276cf05d6c23b5d (diff)
renderer: major refactor; vectors: now aggregates
Diffstat (limited to 'src/renderer/RSoftwareBackend.cpp')
-rw-r--r--src/renderer/RSoftwareBackend.cpp210
1 files changed, 210 insertions, 0 deletions
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;
+ }
+}
+