ref: 6fcd6697335c8180e4571e435eb68d9fc22540e7
dir: /src/render_software.c/
#include "system.h" #include "render.h" #include "mem.h" #include "utils.h" #include "platform.h" #define NEAR_PLANE 16.0 #define FAR_PLANE (RENDER_FADEOUT_FAR) #define TEXTURES_MAX 1024 typedef struct { vec2i_t size; rgba_t *pixels; } render_texture_t; static void line(vec2i_t p0, vec2i_t p1, rgba_t color); static rgba_t *screen_buffer; static int32_t screen_pitch; static int32_t screen_ppr; static vec2i_t screen_size; static mat4_t view_mat = mat4_identity(); static mat4_t mvp_mat = mat4_identity(); static mat4_t projection_mat = mat4_identity(); static mat4_t sprite_mat = mat4_identity(); static render_texture_t textures[TEXTURES_MAX]; static uint32_t textures_len; uint16_t RENDER_NO_TEXTURE; void render_init(vec2i_t screen_size) { render_set_screen_size(screen_size); textures_len = 0; rgba_t white_pixels[4] = { rgba(128,128,128,255), rgba(128,128,128,255), rgba(128,128,128,255), rgba(128,128,128,255) }; RENDER_NO_TEXTURE = render_texture_create(2, 2, white_pixels); } void render_cleanup() {} void render_set_screen_size(vec2i_t size) { screen_size = size; float aspect = (float)size.x / (float)size.y; float fov = (73.75 / 180.0) * 3.14159265358; float f = 1.0 / tan(fov / 2); float nf = 1.0 / (NEAR_PLANE - FAR_PLANE); projection_mat = mat4( f / aspect, 0, 0, 0, 0, f, 0, 0, 0, 0, (FAR_PLANE + NEAR_PLANE) * nf, -1, 0, 0, 2 * FAR_PLANE * NEAR_PLANE * nf, 0 ); } void render_set_resolution(render_resolution_t res) {} void render_set_post_effect(render_post_effect_t post) {} vec2i_t render_size() { return screen_size; } void render_frame_prepare() { screen_buffer = platform_get_screenbuffer(&screen_pitch); screen_ppr = screen_pitch / sizeof(rgba_t); rgba_t color = rgba(0, 0, 0, 255); for (uint32_t y = 0; y < screen_size.y; y++) { for (uint32_t x = 0; x < screen_size.x; x++) { screen_buffer[y * screen_ppr + x] = color; } } } void render_frame_end() {} void render_set_view(vec3_t pos, vec3_t angles) { view_mat = mat4_identity(); mat4_set_translation(&view_mat, vec3(0, 0, 0)); mat4_set_roll_pitch_yaw(&view_mat, vec3(angles.x, -angles.y + M_PI, angles.z + M_PI)); mat4_translate(&view_mat, vec3_inv(pos)); mat4_set_yaw_pitch_roll(&sprite_mat, vec3(-angles.x, angles.y - M_PI, 0)); render_set_model_mat(&mat4_identity()); } void render_set_view_2d() { float near = -1; float far = 1; float left = 0; float right = screen_size.x; float bottom = screen_size.y; float top = 0; float lr = 1 / (left - right); float bt = 1 / (bottom - top); float nf = 1 / (near - far); mvp_mat = mat4( -2 * lr, 0, 0, 0, 0, -2 * bt, 0, 0, 0, 0, 2 * nf, 0, (left + right) * lr, (top + bottom) * bt, (far + near) * nf, 1 ); } void render_set_model_mat(mat4_t *m) { mat4_t vm_mat; mat4_mul(&vm_mat, &view_mat, m); mat4_mul(&mvp_mat, &projection_mat, &vm_mat); } void render_set_depth_write(bool enabled) {} void render_set_depth_test(bool enabled) {} void render_set_depth_offset(float offset) {} void render_set_screen_position(vec2_t pos) {} void render_set_blend_mode(render_blend_mode_t mode) {} void render_set_cull_backface(bool enabled) {} vec3_t render_transform(vec3_t pos) { return vec3_transform(vec3_transform(pos, &view_mat), &projection_mat); } void render_push_tris(tris_t tris, uint16_t texture_index) { float w2 = screen_size.x * 0.5; float h2 = screen_size.y * 0.5; vec3_t p0 = vec3_transform(tris.vertices[0].pos, &mvp_mat); vec3_t p1 = vec3_transform(tris.vertices[1].pos, &mvp_mat); vec3_t p2 = vec3_transform(tris.vertices[2].pos, &mvp_mat); if (p0.z >= 1.0 || p1.z >= 1.0 || p2.z >= 1.0) { return; } vec2i_t sc0 = vec2i(p0.x * w2 + w2, h2 - p0.y * h2); vec2i_t sc1 = vec2i(p1.x * w2 + w2, h2 - p1.y * h2); vec2i_t sc2 = vec2i(p2.x * w2 + w2, h2 - p2.y * h2); rgba_t color = tris.vertices[0].color; color.as_rgba.r = min(color.as_rgba.r * 2, 255); color.as_rgba.g = min(color.as_rgba.g * 2, 255); color.as_rgba.b = min(color.as_rgba.b * 2, 255); color.as_rgba.a = clamp(color.as_rgba.a * (1.0-p0.z) * FAR_PLANE * (2.0/255.0), 0, 255); line(sc0, sc1, color); line(sc1, sc2, color); line(sc2, sc0, color); } void render_push_sprite(vec3_t pos, vec2i_t size, rgba_t color, uint16_t texture_index) { error_if(texture_index >= textures_len, "Invalid texture %d", texture_index); vec3_t p0 = vec3_add(pos, vec3_transform(vec3(-size.x * 0.5, -size.y * 0.5, 0), &sprite_mat)); vec3_t p1 = vec3_add(pos, vec3_transform(vec3( size.x * 0.5, -size.y * 0.5, 0), &sprite_mat)); vec3_t p2 = vec3_add(pos, vec3_transform(vec3(-size.x * 0.5, size.y * 0.5, 0), &sprite_mat)); vec3_t p3 = vec3_add(pos, vec3_transform(vec3( size.x * 0.5, size.y * 0.5, 0), &sprite_mat)); render_texture_t *t = &textures[texture_index]; render_push_tris((tris_t){ .vertices = { {.pos = p0, .uv = {0, 0}, .color = color}, {.pos = p1, .uv = {0 + t->size.x ,0}, .color = color}, {.pos = p2, .uv = {0, 0 + t->size.y}, .color = color}, } }, texture_index); render_push_tris((tris_t){ .vertices = { {.pos = p2, .uv = {0, 0 + t->size.y}, .color = color}, {.pos = p1, .uv = {0 + t->size.x, 0}, .color = color}, {.pos = p3, .uv = {0 + t->size.x, 0 + t->size.y}, .color = color}, } }, texture_index); } void render_push_2d(vec2i_t pos, vec2i_t size, rgba_t color, uint16_t texture_index) { render_push_2d_tile(pos, vec2i(0, 0), render_texture_size(texture_index), size, color, texture_index); } void render_push_2d_tile(vec2i_t pos, vec2i_t uv_offset, vec2i_t uv_size, vec2i_t size, rgba_t color, uint16_t texture_index) { error_if(texture_index >= textures_len, "Invalid texture %d", texture_index); render_push_tris((tris_t){ .vertices = { {.pos = {pos.x, pos.y + size.y, 0}, .uv = {uv_offset.x , uv_offset.y + uv_size.y}, .color = color}, {.pos = {pos.x + size.x, pos.y, 0}, .uv = {uv_offset.x + uv_size.x, uv_offset.y}, .color = color}, {.pos = {pos.x, pos.y, 0}, .uv = {uv_offset.x , uv_offset.y}, .color = color}, } }, texture_index); render_push_tris((tris_t){ .vertices = { {.pos = {pos.x + size.x, pos.y + size.y, 0}, .uv = {uv_offset.x + uv_size.x, uv_offset.y + uv_size.y}, .color = color}, {.pos = {pos.x + size.x, pos.y, 0}, .uv = {uv_offset.x + uv_size.x, uv_offset.y}, .color = color}, {.pos = {pos.x, pos.y + size.y, 0}, .uv = {uv_offset.x , uv_offset.y + uv_size.y}, .color = color}, } }, texture_index); } uint16_t render_texture_create(uint32_t width, uint32_t height, rgba_t *pixels) { error_if(textures_len >= TEXTURES_MAX, "TEXTURES_MAX reached"); uint32_t byte_size = width * height * sizeof(rgba_t); uint16_t texture_index = textures_len; textures[texture_index] = (render_texture_t){{width, height}, NULL}; // textures[texture_index] = (render_texture_t){{width, height}, mem_bump(byte_size)}; // memcpy(textures[texture_index].pixels, pixels, byte_size); textures_len++; return texture_index; } vec2i_t render_texture_size(uint16_t texture_index) { error_if(texture_index >= textures_len, "Invalid texture %d", texture_index); return textures[texture_index].size; } void render_texture_replace_pixels(int16_t texture_index, rgba_t *pixels) { error_if(texture_index >= textures_len, "Invalid texture %d", texture_index); render_texture_t *t = &textures[texture_index]; // memcpy(t->pixels, pixels, t->size.x * t->size.y * sizeof(rgba_t)); } uint16_t render_textures_len() { return textures_len; } void render_textures_reset(uint16_t len) { error_if(len > textures_len, "Invalid texture reset len %d >= %d", len, textures_len); textures_len = len; } void render_textures_dump(const char *path) {} // ----------------------------------------------------------------------------- static inline rgba_t color_mix(rgba_t in, rgba_t out) { return rgba( lerp(in.as_rgba.r, out.as_rgba.r, out.as_rgba.a/255.0), lerp(in.as_rgba.g, out.as_rgba.g, out.as_rgba.a/255.0), lerp(in.as_rgba.b, out.as_rgba.b, out.as_rgba.a/255.0), 1 ); } typedef enum { CLIP_INSIDE = 0, CLIP_LEFT = (1<<0), CLIP_RIGHT = (1<<1), CLIP_BOTTOM = (1<<2), CLIP_TOP = (1<<3), } clip_code_t; static inline clip_code_t clip_code(vec2i_t p) { clip_code_t cc = CLIP_INSIDE; if (p.x < 0) { flags_add(cc, CLIP_LEFT); } else if (p.x >= screen_size.x) { flags_add(cc, CLIP_RIGHT); } if (p.y < 0) { flags_add(cc, CLIP_BOTTOM); } else if (p.y >= screen_size.y) { flags_add(cc, CLIP_TOP); } return cc; } static void line(vec2i_t p0, vec2i_t p1, rgba_t color) { // Cohen Sutherland Line Clipping clip_code_t cc0 = clip_code(p0); clip_code_t cc1 = clip_code(p1); bool accept = false; vec2i_t ss = vec2i(screen_size.x-1, screen_size.y-1); while (true) { if (!(cc0 | cc1)) { accept = true; break; } else if (cc0 & cc1) { break; } else { vec2i_t r = p0; clip_code_t cc_out = cc0 ? cc0 : cc1; if (flags_is(cc_out, CLIP_TOP)) { r.x = p0.x + (p1.x - p0.x) * (ss.y - p0.y) / (p1.y - p0.y); r.y = ss.y; } else if (flags_is(cc_out, CLIP_BOTTOM)) { r.x = p0.x + (p1.x - p0.x) * (-p0.y) / (p1.y - p0.y); r.y = 0; } else if (flags_is(cc_out, CLIP_RIGHT)) { r.y = p0.y + (p1.y - p0.y) * (ss.x - p0.x) / (p1.x - p0.x); r.x = ss.x; } else if (flags_is(cc_out, CLIP_LEFT)) { r.y = p0.y + (p1.y - p0.y) * (-p0.x) / (p1.x - p0.x); r.x = 0; } if (cc_out == cc0) { p0.x = r.x; p0.y = r.y; cc0 = clip_code(p0); } else { p1.x = r.x; p1.y = r.y; cc1 = clip_code(p1); } } } if (!accept) { return; } // Bresenham's line algorithm bool steep = false; if (abs(p0.x - p1.x) < abs(p0.y - p1.y)) { swap(p0.x, p0.y); swap(p1.x, p1.y); steep = true; } if (p0.x > p1.x) { swap(p0.x, p1.x); swap(p0.y, p1.y); } int32_t dx = p1.x - p0.x; int32_t dy = p1.y - p0.y; int32_t derror2 = abs(dy) * 2; int32_t error2 = 0; int32_t y = p0.y; int32_t ydir = (p1.y > p0.y ? 1 : -1); if (steep) { for (int32_t x = p0.x; x <= p1.x; x++) { screen_buffer[x * screen_ppr + y] = color_mix(screen_buffer[x * screen_ppr + y], color); error2 += derror2; if (error2 > dx) { y += ydir; error2 -= dx * 2; } } } else { for (int32_t x = p0.x; x <= p1.x; x++) { screen_buffer[y * screen_ppr + x] = color_mix(screen_buffer[y * screen_ppr + x], color); error2 += derror2; if (error2 > dx) { y += ydir; error2 -= dx * 2; } } } }