Something went wrong. Try again.
Software renderer in C
Something went wrong. Try again.
23 kB · 618 lines
C
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619#include "ve_renderer.h"#include "backend.h"#include "defs.h"#include "logger.h"#include <assert.h>#include <math.h>#include <stdint.h>#include <stdio.h>#include <string.h>
char font8x8_basic[128][8] = { {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0000 (nul) {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0001 {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0002 {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0003 {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0004 {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0005 {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0006 {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0007 {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0008 {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0009 {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000A {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000B {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000C {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000D {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000E {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000F {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0010 {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0011 {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0012 {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0013 {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0014 {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0015 {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0016 {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0017 {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0018 {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0019 {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001A {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001B {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001C {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001D {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001E {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001F {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0020 (space) {0x18, 0x3C, 0x3C, 0x18, 0x18, 0x00, 0x18, 0x00}, // U+0021 (!) {0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0022 (") {0x36, 0x36, 0x7F, 0x36, 0x7F, 0x36, 0x36, 0x00}, // U+0023 (#) {0x0C, 0x3E, 0x03, 0x1E, 0x30, 0x1F, 0x0C, 0x00}, // U+0024 ($) {0x00, 0x63, 0x33, 0x18, 0x0C, 0x66, 0x63, 0x00}, // U+0025 (%) {0x1C, 0x36, 0x1C, 0x6E, 0x3B, 0x33, 0x6E, 0x00}, // U+0026 (&) {0x06, 0x06, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0027 (') {0x18, 0x0C, 0x06, 0x06, 0x06, 0x0C, 0x18, 0x00}, // U+0028 (() {0x06, 0x0C, 0x18, 0x18, 0x18, 0x0C, 0x06, 0x00}, // U+0029 ()) {0x00, 0x66, 0x3C, 0xFF, 0x3C, 0x66, 0x00, 0x00}, // U+002A (*) {0x00, 0x0C, 0x0C, 0x3F, 0x0C, 0x0C, 0x00, 0x00}, // U+002B (+) {0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x06}, // U+002C (,) {0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00}, // U+002D (-) {0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x00}, // U+002E (.) {0x60, 0x30, 0x18, 0x0C, 0x06, 0x03, 0x01, 0x00}, // U+002F (/) {0x3E, 0x63, 0x73, 0x7B, 0x6F, 0x67, 0x3E, 0x00}, // U+0030 (0) {0x0C, 0x0E, 0x0C, 0x0C, 0x0C, 0x0C, 0x3F, 0x00}, // U+0031 (1) {0x1E, 0x33, 0x30, 0x1C, 0x06, 0x33, 0x3F, 0x00}, // U+0032 (2) {0x1E, 0x33, 0x30, 0x1C, 0x30, 0x33, 0x1E, 0x00}, // U+0033 (3) {0x38, 0x3C, 0x36, 0x33, 0x7F, 0x30, 0x78, 0x00}, // U+0034 (4) {0x3F, 0x03, 0x1F, 0x30, 0x30, 0x33, 0x1E, 0x00}, // U+0035 (5) {0x1C, 0x06, 0x03, 0x1F, 0x33, 0x33, 0x1E, 0x00}, // U+0036 (6) {0x3F, 0x33, 0x30, 0x18, 0x0C, 0x0C, 0x0C, 0x00}, // U+0037 (7) {0x1E, 0x33, 0x33, 0x1E, 0x33, 0x33, 0x1E, 0x00}, // U+0038 (8) {0x1E, 0x33, 0x33, 0x3E, 0x30, 0x18, 0x0E, 0x00}, // U+0039 (9) {0x00, 0x0C, 0x0C, 0x00, 0x00, 0x0C, 0x0C, 0x00}, // U+003A (:) {0x00, 0x0C, 0x0C, 0x00, 0x00, 0x0C, 0x0C, 0x06}, // U+003B (;) {0x18, 0x0C, 0x06, 0x03, 0x06, 0x0C, 0x18, 0x00}, // U+003C (<) {0x00, 0x00, 0x3F, 0x00, 0x00, 0x3F, 0x00, 0x00}, // U+003D (=) {0x06, 0x0C, 0x18, 0x30, 0x18, 0x0C, 0x06, 0x00}, // U+003E (>) {0x1E, 0x33, 0x30, 0x18, 0x0C, 0x00, 0x0C, 0x00}, // U+003F (?) {0x3E, 0x63, 0x7B, 0x7B, 0x7B, 0x03, 0x1E, 0x00}, // U+0040 (@) {0x0C, 0x1E, 0x33, 0x33, 0x3F, 0x33, 0x33, 0x00}, // U+0041 (A) {0x3F, 0x66, 0x66, 0x3E, 0x66, 0x66, 0x3F, 0x00}, // U+0042 (B) {0x3C, 0x66, 0x03, 0x03, 0x03, 0x66, 0x3C, 0x00}, // U+0043 (C) {0x1F, 0x36, 0x66, 0x66, 0x66, 0x36, 0x1F, 0x00}, // U+0044 (D) {0x7F, 0x46, 0x16, 0x1E, 0x16, 0x46, 0x7F, 0x00}, // U+0045 (E) {0x7F, 0x46, 0x16, 0x1E, 0x16, 0x06, 0x0F, 0x00}, // U+0046 (F) {0x3C, 0x66, 0x03, 0x03, 0x73, 0x66, 0x7C, 0x00}, // U+0047 (G) {0x33, 0x33, 0x33, 0x3F, 0x33, 0x33, 0x33, 0x00}, // U+0048 (H) {0x1E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+0049 (I) {0x78, 0x30, 0x30, 0x30, 0x33, 0x33, 0x1E, 0x00}, // U+004A (J) {0x67, 0x66, 0x36, 0x1E, 0x36, 0x66, 0x67, 0x00}, // U+004B (K) {0x0F, 0x06, 0x06, 0x06, 0x46, 0x66, 0x7F, 0x00}, // U+004C (L) {0x63, 0x77, 0x7F, 0x7F, 0x6B, 0x63, 0x63, 0x00}, // U+004D (M) {0x63, 0x67, 0x6F, 0x7B, 0x73, 0x63, 0x63, 0x00}, // U+004E (N) {0x1C, 0x36, 0x63, 0x63, 0x63, 0x36, 0x1C, 0x00}, // U+004F (O) {0x3F, 0x66, 0x66, 0x3E, 0x06, 0x06, 0x0F, 0x00}, // U+0050 (P) {0x1E, 0x33, 0x33, 0x33, 0x3B, 0x1E, 0x38, 0x00}, // U+0051 (Q) {0x3F, 0x66, 0x66, 0x3E, 0x36, 0x66, 0x67, 0x00}, // U+0052 (R) {0x1E, 0x33, 0x07, 0x0E, 0x38, 0x33, 0x1E, 0x00}, // U+0053 (S) {0x3F, 0x2D, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+0054 (T) {0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x3F, 0x00}, // U+0055 (U) {0x33, 0x33, 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x00}, // U+0056 (V) {0x63, 0x63, 0x63, 0x6B, 0x7F, 0x77, 0x63, 0x00}, // U+0057 (W) {0x63, 0x63, 0x36, 0x1C, 0x1C, 0x36, 0x63, 0x00}, // U+0058 (X) {0x33, 0x33, 0x33, 0x1E, 0x0C, 0x0C, 0x1E, 0x00}, // U+0059 (Y) {0x7F, 0x63, 0x31, 0x18, 0x4C, 0x66, 0x7F, 0x00}, // U+005A (Z) {0x1E, 0x06, 0x06, 0x06, 0x06, 0x06, 0x1E, 0x00}, // U+005B ([) {0x03, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x40, 0x00}, // U+005C (\) {0x1E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1E, 0x00}, // U+005D (]) {0x08, 0x1C, 0x36, 0x63, 0x00, 0x00, 0x00, 0x00}, // U+005E (^) {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF}, // U+005F (_) {0x0C, 0x0C, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0060 (`) {0x00, 0x00, 0x1E, 0x30, 0x3E, 0x33, 0x6E, 0x00}, // U+0061 (a) {0x07, 0x06, 0x06, 0x3E, 0x66, 0x66, 0x3B, 0x00}, // U+0062 (b) {0x00, 0x00, 0x1E, 0x33, 0x03, 0x33, 0x1E, 0x00}, // U+0063 (c) {0x38, 0x30, 0x30, 0x3e, 0x33, 0x33, 0x6E, 0x00}, // U+0064 (d) {0x00, 0x00, 0x1E, 0x33, 0x3f, 0x03, 0x1E, 0x00}, // U+0065 (e) {0x1C, 0x36, 0x06, 0x0f, 0x06, 0x06, 0x0F, 0x00}, // U+0066 (f) {0x00, 0x00, 0x6E, 0x33, 0x33, 0x3E, 0x30, 0x1F}, // U+0067 (g) {0x07, 0x06, 0x36, 0x6E, 0x66, 0x66, 0x67, 0x00}, // U+0068 (h) {0x0C, 0x00, 0x0E, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+0069 (i) {0x30, 0x00, 0x30, 0x30, 0x30, 0x33, 0x33, 0x1E}, // U+006A (j) {0x07, 0x06, 0x66, 0x36, 0x1E, 0x36, 0x67, 0x00}, // U+006B (k) {0x0E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+006C (l) {0x00, 0x00, 0x33, 0x7F, 0x7F, 0x6B, 0x63, 0x00}, // U+006D (m) {0x00, 0x00, 0x1F, 0x33, 0x33, 0x33, 0x33, 0x00}, // U+006E (n) {0x00, 0x00, 0x1E, 0x33, 0x33, 0x33, 0x1E, 0x00}, // U+006F (o) {0x00, 0x00, 0x3B, 0x66, 0x66, 0x3E, 0x06, 0x0F}, // U+0070 (p) {0x00, 0x00, 0x6E, 0x33, 0x33, 0x3E, 0x30, 0x78}, // U+0071 (q) {0x00, 0x00, 0x3B, 0x6E, 0x66, 0x06, 0x0F, 0x00}, // U+0072 (r) {0x00, 0x00, 0x3E, 0x03, 0x1E, 0x30, 0x1F, 0x00}, // U+0073 (s) {0x08, 0x0C, 0x3E, 0x0C, 0x0C, 0x2C, 0x18, 0x00}, // U+0074 (t) {0x00, 0x00, 0x33, 0x33, 0x33, 0x33, 0x6E, 0x00}, // U+0075 (u) {0x00, 0x00, 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x00}, // U+0076 (v) {0x00, 0x00, 0x63, 0x6B, 0x7F, 0x7F, 0x36, 0x00}, // U+0077 (w) {0x00, 0x00, 0x63, 0x36, 0x1C, 0x36, 0x63, 0x00}, // U+0078 (x) {0x00, 0x00, 0x33, 0x33, 0x33, 0x3E, 0x30, 0x1F}, // U+0079 (y) {0x00, 0x00, 0x3F, 0x19, 0x0C, 0x26, 0x3F, 0x00}, // U+007A (z) {0x38, 0x0C, 0x0C, 0x07, 0x0C, 0x0C, 0x38, 0x00}, // U+007B ({) {0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00}, // U+007C (|) {0x07, 0x0C, 0x0C, 0x38, 0x0C, 0x0C, 0x07, 0x00}, // U+007D (}) {0x6E, 0x3B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+007E (~) {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} // U+007F};
uint32_t ve_alpha_blend(u32 background_packed, Color foreground) { Color background; background.packed = background_packed;
uint32_t alpha = foreground.a; uint32_t inv_alpha = 255 - alpha;
uint8_t r = (foreground.r * alpha + background.r * inv_alpha) / 255; uint8_t g = (foreground.g * alpha + background.g * inv_alpha) / 255; uint8_t b = (foreground.b * alpha + background.b * inv_alpha) / 255;
return COLOR(r, g, b, 255).packed;}
void ve_set_pixel(Framebuffer *fb, i32 x, i32 y, Color Color) { if (x < 0 || x >= fb->width || y < 0 || y >= fb->height) { LOG_ONCE("Trying to put pixel outside framebuffer", WARNING); return; };
u32 *target_pixel = (uint32_t *)((uint8_t *)fb->pixels + y * fb->pitch + x * sizeof(uint32_t));
if (Color.a == 255) { *target_pixel = Color.packed; } else if (Color.a > 0) { *target_pixel = ve_alpha_blend(*target_pixel, Color); }}
// Interpolates integer values between (x0, y0) and (x1, y1)// Calculates a linear sequence of values for each integer step from x0 to x1.V2 ve_interpolate(V2 start, V2 end, f32 t) { V2 p; p.x = start.x + (end.x - start.x) * t; p.y = start.y + (end.y - start.y) * t; return p;};
V2 ve_bezier_interpolate(V2 p0, V2 p1, V2 p2, f32 t) { V2 intermediate_a = ve_interpolate(p0, p1, t); V2 intermediate_b = ve_interpolate(p1, p2, t); return ve_interpolate(intermediate_a, intermediate_b, t);};
V2 v2_rotate(V2 v, V2 center, float angle) { V2 rotated; v.x -= center.x; v.y -= center.y; rotated.x = v.x * cos(angle) - v.y * sin(angle); rotated.y = v.x * sin(angle) + v.y * cos(angle); rotated.x += center.x; rotated.y += center.y; return rotated;};
// Simple line rasterization algorithmvoid ve_draw_line(Framebuffer *fb, V2 p0, V2 p1, Color color) { f32 dx = p1.x - p0.x; f32 dy = p1.y - p0.y; f32 steps = fmaxf(fabsf(dx), fabsf(dy));
f32 x_inc = dx / steps; f32 y_inc = dy / steps;
f32 x = p0.x; f32 y = p0.y;
for (i32 i = 0; i <= (i32)steps; i++) { ve_set_pixel(fb, (i32)roundf(x), (i32)roundf(y), color); x += x_inc; y += y_inc; }}
// Implementation of Bresenham's line algorithmvoid ve_draw_line_fast(Framebuffer *fb, V2 p0, V2 p1, Color color) { f32 x0 = p0.x, y0 = p0.y, x1 = p1.x, y1 = p1.y; bool steep = fabsf(y1 - y0) > fabsf(x1 - x0);
if (steep) { SWAP(x0, y0); SWAP(x1, y1); } if (x0 > x1) { SWAP(x0, x1); SWAP(y0, y1); }
f32 dx = x1 - x0; f32 dy = y1 - y0; f32 yi = 1; if (dy < 0) { yi = -1; dy = -dy; }; f32 D = (2 * dy) - dx; f32 y = y0;
for (i32 x = x0; x <= x1; x++) { if (steep) { ve_set_pixel(fb, y, x, color); } else { ve_set_pixel(fb, x, y, color); }; if (D > 0) { y += yi; D += (2 * (dy - dx)); } else { D += 2 * dy; }; };};
// Draw anti-aliased line using Xiaolin Wu's line algorithmvoid ve_draw_line_aa(Framebuffer *fb, V2 p0, V2 p1, Color color) { f32 x0 = p0.x; f32 y0 = p0.y; f32 x1 = p1.x; f32 y1 = p1.y;
i32 steep = fabsf(y1 - y0) > fabsf(x1 - x0);
if (steep) { SWAP(x0, y0); SWAP(x1, y1); }
if (x0 > x1) { SWAP(x0, x1); SWAP(y0, y1); }
f32 dx = x1 - x0; f32 dy = y1 - y0;
f32 gradient; if (dx == 0.0f) { gradient = 1.0f; } else { gradient = dy / dx; }
// Handle first endpoint f32 xend = floorf(x0); f32 yend = y0 + gradient * (xend - x0); f32 xgap = RF_PART(x0); i32 xpxl1 = (i32)xend; i32 ypxl1 = I_PART(yend);
if (steep) { Color c1 = COLOR(color.r, color.g, color.b, (u8)(RF_PART(yend) * xgap * 255)); Color c2 = COLOR(color.r, color.g, color.b, (u8)(F_PART(yend) * xgap * 255)); ve_set_pixel(fb, ypxl1, xpxl1, c1); ve_set_pixel(fb, ypxl1 + 1, xpxl1, c2); } else { Color c1 = COLOR(color.r, color.g, color.b, (u8)(RF_PART(yend) * xgap * 255)); Color c2 = COLOR(color.r, color.g, color.b, (u8)(F_PART(yend) * xgap * 255)); ve_set_pixel(fb, xpxl1, ypxl1, c1); ve_set_pixel(fb, xpxl1, ypxl1 + 1, c2); }
f32 intery = yend + gradient;
// Handle second endpoint xend = ceilf(x1); yend = y1 + gradient * (xend - x1); xgap = F_PART(x1); i32 xpxl2 = (i32)xend; i32 ypxl2 = I_PART(yend);
if (steep) { Color c1 = COLOR(color.r, color.g, color.b, (u8)(RF_PART(yend) * xgap * 255)); Color c2 = COLOR(color.r, color.g, color.b, (u8)(F_PART(yend) * xgap * 255)); ve_set_pixel(fb, ypxl2, xpxl2, c1); ve_set_pixel(fb, ypxl2 + 1, xpxl2, c2); } else { Color c1 = COLOR(color.r, color.g, color.b, (u8)(RF_PART(yend) * xgap * 255)); Color c2 = COLOR(color.r, color.g, color.b, (u8)(F_PART(yend) * xgap * 255)); ve_set_pixel(fb, xpxl2, ypxl2, c1); ve_set_pixel(fb, xpxl2, ypxl2 + 1, c2); }
// Main loop if (steep) { for (i32 x = xpxl1 + 1; x < xpxl2; x++) { Color c1 = COLOR(color.r, color.g, color.b, (u8)(RF_PART(intery) * 255)); Color c2 = COLOR(color.r, color.g, color.b, (u8)(F_PART(intery) * 255)); ve_set_pixel(fb, I_PART(intery), x, c1); ve_set_pixel(fb, I_PART(intery) + 1, x, c2); intery += gradient; } } else { for (i32 x = xpxl1 + 1; x < xpxl2; x++) { Color c1 = COLOR(color.r, color.g, color.b, (u8)(RF_PART(intery) * 255)); Color c2 = COLOR(color.r, color.g, color.b, (u8)(F_PART(intery) * 255)); ve_set_pixel(fb, x, I_PART(intery), c1); ve_set_pixel(fb, x, I_PART(intery) + 1, c2); intery += gradient; } }}
void ve_draw_bezier(Framebuffer *fb, V2 p0, V2 p1, V2 p2, Color color) { V2 p_init = p0; i32 steps = 30; // WARN: hard coded value for (i32 i = 0; i < steps; i++) { f32 t = (f32)i / steps; V2 p_next = ve_bezier_interpolate(p0, p1, p2, t); ve_draw_line(fb, p_init, p_next, color); p_init = p_next; };};
void ve_draw_triangle_wireframe(Framebuffer *fb, V2 p0, V2 p1, V2 p2, Color Color) { ve_draw_line(fb, p0, p1, Color); ve_draw_line(fb, p1, p2, Color); ve_draw_line(fb, p2, p0, Color);};
float ve_edge_func(V2 p0, V2 p1, V2 a) { return (a.x - p0.x) * (p1.y - p0.y) - (a.y - p0.y) * (p1.x - p0.x);};
float ve_min(float *nums, int N) { int min = nums[0]; for (int i = 1; i < N; i++) { if (nums[i] < min) { min = nums[i]; }; }; return min;};
float ve_max(float *nums, int N) { int max = nums[0]; for (int i = 1; i < N; i++) { if (nums[i] > max) { max = nums[i]; }; }; return max;};
// Check to apply top_let convention for triangle rasterizationbool is_top_left(V2 p0, V2 p1) { V2 edge = {p1.x - p0.x, p1.y - p0.y}; bool is_top_edge = edge.y == 0 && edge.x > 0; bool is_left_edge = edge.y < 0; return is_top_edge || is_left_edge;};
void ve_draw_triangle(Framebuffer *fb, V2 p0, V2 p1, V2 p2, Color Color) { // Bounding Box int x_min = floor(ve_min((float[]){p0.x, p1.x, p2.x, 0}, 4)); int x_max = ceil(ve_max((float[]){p0.x, p1.x, p2.x, fb->width}, 4)); int y_min = floor(ve_min((float[]){p0.y, p1.y, p2.y, 0}, 4)); int y_max = ceil(ve_max((float[]){p0.y, p1.y, p2.y, fb->height}, 4));
float dy01 = p1.y - p0.y; float dx01 = p0.x - p1.x;
float dy12 = p2.y - p1.y; float dx12 = p1.x - p2.x;
float dy20 = p0.y - p2.y; float dx20 = p2.x - p0.x;
float bias0 = is_top_left(p0, p1) ? 0 : -0.0001; float bias1 = is_top_left(p1, p2) ? 0 : -0.0001; float bias2 = is_top_left(p2, p0) ? 0 : -0.0001;
V2 p_min = {x_min + 0.5f, y_min + 0.5f}; float w01_row = ve_edge_func(p0, p1, p_min) + bias0; float w12_row = ve_edge_func(p1, p2, p_min) + bias1; float w20_row = ve_edge_func(p2, p0, p_min) + bias2;
for (int y = y_min; y <= y_max; y++) { float w01 = w01_row; float w12 = w12_row; float w20 = w20_row;
for (int x = x_min; x <= x_max; x++) { if ((w01 >= 0) && (w12 >= 0) && (w20 >= 0)) { ve_set_pixel(fb, x, y, Color); }
w01 += dy01; w12 += dy12; w20 += dy20; } w01_row += dx01; w12_row += dx12; w20_row += dx20; }}
void ve_draw_triangle_gradient(Framebuffer *fb, V2 p0, V2 p1, V2 p2, Color Colors[3]) { int x_min = floor(ve_min((float[]){p0.x, p1.x, p2.x, 0}, 4)); int x_max = ceil(ve_max((float[]){p0.x, p1.x, p2.x, fb->width}, 4)); int y_min = floor(ve_min((float[]){p0.y, p1.y, p2.y, 0}, 4)); int y_max = ceil(ve_max((float[]){p0.y, p1.y, p2.y, fb->height}, 4));
float dy01 = p1.y - p0.y; float dx01 = p0.x - p1.x;
float dy12 = p2.y - p1.y; float dx12 = p1.x - p2.x;
float dy20 = p0.y - p2.y; float dx20 = p2.x - p0.x;
float bias0 = is_top_left(p0, p1) ? 0 : -0.001; float bias1 = is_top_left(p1, p2) ? 0 : -0.001; float bias2 = is_top_left(p2, p0) ? 0 : -0.001;
V2 p_min = {x_min + 0.5f, y_min + 0.5f}; float w01_row = ve_edge_func(p0, p1, p_min) + bias0; float w12_row = ve_edge_func(p1, p2, p_min) + bias1; float w20_row = ve_edge_func(p2, p0, p_min) + bias2;
float area = ve_edge_func(p0, p1, p2); for (int y = y_min; y < y_max; y++) { float w01 = w01_row; float w12 = w12_row; float w20 = w20_row; for (int x = x_min; x < x_max; x++) {
float alpha = w01 / area; float beta = w12 / area; float gamma = w20 / area; if ((w01 >= 0) && (w12 >= 0) && (w20 >= 0)) { int r = (alpha)*Colors[0].r + (beta)*Colors[1].r + (gamma)*Colors[2].r; int g = (alpha)*Colors[0].g + (beta)*Colors[1].g + (gamma)*Colors[2].g; int b = (alpha)*Colors[0].b + (beta)*Colors[1].b + (gamma)*Colors[2].b; Color gradient_Color = {{r, g, b, 255}}; ve_set_pixel(fb, x, y, gradient_Color); }; w01 += dy01; w12 += dy12; w20 += dy20; } w01_row += dx01; w12_row += dx12; w20_row += dx20; };};
void ve_draw_circle(Framebuffer *fb, V2 p0, int r, Color Color) { int x = -r, y = 0, err = 2 - 2 * r; /* II. Quadrant */ do { ve_set_pixel(fb, p0.x - x, p0.y + y, Color); /* I. Quadrant */ ve_set_pixel(fb, p0.x - y, p0.y - x, Color); /* II. Quadrant */ ve_set_pixel(fb, p0.x + x, p0.y - y, Color); /* III. Quadrant */ ve_set_pixel(fb, p0.x + y, p0.y + x, Color); /* IV. Quadrant */ r = err; if (r <= y) err += ++y * 2 + 1; /* e_xy+e_y < 0 */ if (r > x || err > y) err += ++x * 2 + 1; /* e_xy+e_x > 0 or no 2nd y-step */ } while (x < 0);};
void ve_draw_ellipse(Framebuffer *fb, V2 p0, V2 p1, Color Color) { int a = fabs(p1.x - p0.x), b = fabs(p1.y - p0.y), b1 = b & 1; /* values of diameter */ long dx = 4 * (1 - a) * b * b, dy = 4 * (b1 + 1) * a * a; /* error increment */ long err = dx + dy + b1 * a * a, e2; /* error of 1.step */
if (p0.x > p1.x) { p0.x = p1.x; p1.x += a; } /* if called with swapped points */ if (p0.y > p1.y) p0.y = p1.y; /* .. exchange them */ p0.y += (float)(b + 1) / 2; p1.y = p0.y - b1; /* starting pixel */ a *= 8 * a; b1 = 8 * b * b;
do { ve_set_pixel(fb, p1.x, p0.y, Color); /* I. Quadrant */ ve_set_pixel(fb, p0.x, p0.y, Color); /* II. Quadrant */ ve_set_pixel(fb, p0.x, p1.y, Color); /* III. Quadrant */ ve_set_pixel(fb, p1.x, p1.y, Color); /* IV. Quadrant */ e2 = 2 * err; if (e2 <= dy) { p0.y++; p1.y--; err += dy += a; } /* y step */ if (e2 >= dx || 2 * err > dy) { p0.x++; p1.x--; err += dx += b1; } /* x step */ } while (p0.x <= p1.x);
while (p0.y - p1.y < b) { /* too early stop of flat ellipses a=1 */ ve_set_pixel(fb, p0.x - 1, p0.y, Color); /* -> finish tip of ellipse */ ve_set_pixel(fb, p1.x + 1, p0.y++, Color); ve_set_pixel(fb, p0.x - 1, p1.y, Color); ve_set_pixel(fb, p1.x + 1, p1.y--, Color); }};
void ve_render_bitmap_char(Framebuffer *fb, char c, V2 p, Color color, u8 font_size) { if (font_size <= 0) { return; }; for (i32 row = 0; row < 8 * font_size; row++) { u8 byte = font8x8_basic[(i32)c][row / font_size]; for (i32 col = 0; col < 8 * font_size; col++) { i32 col_effective = col / font_size; u8 pixel = (byte >> col_effective) & 1; Color pix_col = COLOR(color.r * pixel, color.g * pixel, color.b * pixel, color.a * pixel); ve_set_pixel(fb, p.x + col, p.y + row, pix_col); }; };};
void ve_render_bitmap_str(Framebuffer *fb, char *str, V2 p, Color color, u8 font_size) { V2 pen = p; while (*str) { if (*str == '\n') { pen.x = p.x; pen.y += 8 * font_size; } else { ve_render_bitmap_char(fb, *str, pen, color, font_size); pen.x += 8 * font_size; } str++; }};
void ve_render_fps(ve_context *ctx) { char fps_str[64]; u32 fps = 1 / ctx->dt; snprintf(fps_str, sizeof(fps_str), "fps: %d", fps);
V2 origin = {0, 0}; Color white = COLOR(255, 255, 255, 255); Framebuffer fb = ctx->ve_fb; ve_render_bitmap_str(&fb, fps_str, origin, white, 1);};
void ve_clear(Framebuffer *fb, Color Color) { // Si todos los canales son iguales (ej: negro 0,0,0 o blanco 255,255,255) // podemos usar memset para llenar toda la memoria instantáneamente. if (Color.r == Color.g && Color.g == Color.b && Color.b == Color.a) { memset(fb->pixels, Color.r, fb->height * fb->pitch); } else { // Si el Color no es uniforme, tenemos que iterar. // Optimizamos tratando el buffer como un arreglo de uint32_t. uint32_t *pixels = (uint32_t *)fb->pixels; uint32_t packed_Color = Color.packed; int total_pixels = fb->width * fb->height;
for (int i = 0; i < total_pixels; i++) { pixels[i] = packed_Color; } }}