From 39100e7292d3ee12d387fddfa0f0d7b712e31e1c Mon Sep 17 00:00:00 2001 From: quou Date: Sun, 30 Jun 2024 18:24:01 +1000 Subject: initial commit. --- .gitignore | 4 + 3de.c | 45 +++++++ Makefile | 32 +++++ config.h | 7 ++ error.h | 11 ++ maths.c | 115 +++++++++++++++++ maths.h | 17 +++ memory.c | 222 +++++++++++++++++++++++++++++++++ memory.h | 56 +++++++++ plat.c | 413 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ plat.h | 58 +++++++++ rect.c | 46 +++++++ rect.h | 11 ++ render.c | 207 +++++++++++++++++++++++++++++++ render.h | 48 +++++++ 15 files changed, 1292 insertions(+) create mode 100644 .gitignore create mode 100644 3de.c create mode 100644 Makefile create mode 100644 config.h create mode 100644 error.h create mode 100644 maths.c create mode 100644 maths.h create mode 100644 memory.c create mode 100644 memory.h create mode 100644 plat.c create mode 100644 plat.h create mode 100644 rect.c create mode 100644 rect.h create mode 100644 render.c create mode 100644 render.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..60455eb --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +tags +*.d +*.o +3de diff --git a/3de.c b/3de.c new file mode 100644 index 0000000..1b88ae3 --- /dev/null +++ b/3de.c @@ -0,0 +1,45 @@ +#include "config.h" +#include "memory.h" +#include "plat.h" +#include "render.h" + +#include + +int entrypoint(int argc, const char** argv, Arena* a) { + App* app; + Renderer r; + Heap h; + FPS f; + char buf[32]; + Colour blue = make_aliceblue(); + (void)argc; + (void)argv; + init_heap( + &h, + arena_alloc(a, app_memory_size), + app_memory_size + ); + app = new_app(&h, 640, 480, "3D Engine"); + if (!app) return app->err; + init_fps(&f, 20); + while (app->o) { + fps_begin(&f); + while (f.now >= f.next) { + app_begin(app); + ren_begin(&r, app->fb, app->w, app->h); + ren_clear(&r); + sprintf(buf, "FPS: %d", app->fps); + ren_text(&r, blue, 3, 3, buf); + sprintf(buf, "CAP: %d", f.fps); + ren_text(&r, blue, 3, 13, buf); + sprintf(buf, "MOUSE: %d, %d", app->mx, app->my); + ren_text(&r, blue, 3, 23, buf); + ren_end(&r); + app_end(app); + fps_update(&f); + } + fps_end(&f); + } + deinit_app(app); + return 0; +} diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..8b774cd --- /dev/null +++ b/Makefile @@ -0,0 +1,32 @@ +compiler = gcc +linker = gcc +cflags = -I./ -g -DDEBUG -Dplat_x11 -Dplat_x86 \ +-Dplat_posix -Dallocation_default_alignment=8 \ +-Wall -Wextra -pedantic -std=c90 +lflags = -lX11 +target = 3de + +sources = \ + 3de.c \ + maths.c \ + memory.c \ + plat.c \ + rect.c \ + render.c + +objects = $(sources:%.c=%.o) + +all: $(target) + +$(objects): %.o : %.c + $(compiler) -MMD -MF $(basename $@).d $(cflags) -o $@ -c $< + +$(target): $(objects) + $(linker) $(objects) -o $@ $(lflags) + +clean: + rm *.d + rm *.o + rm $(target) + +-include $(sources:%.c=%.d) diff --git a/config.h b/config.h new file mode 100644 index 0000000..96094dd --- /dev/null +++ b/config.h @@ -0,0 +1,7 @@ +#ifndef config_h +#define config_h + +#define memory_size (1024 * 1024 * 32) +#define app_memory_size (1024 * 1024 * 16) + +#endif diff --git a/error.h b/error.h new file mode 100644 index 0000000..8ef761b --- /dev/null +++ b/error.h @@ -0,0 +1,11 @@ +#ifndef error_h +#define error_h + +typedef enum { + error_none, + error_assertion_failed, + error_platform_error, + error_out_of_memory +} Error; + +#endif diff --git a/maths.c b/maths.c new file mode 100644 index 0000000..d0bccf3 --- /dev/null +++ b/maths.c @@ -0,0 +1,115 @@ +#include "maths.h" + +#define sin_table_count 256 + +static const int sin_table[sin_table_count] = { + 0, 25, 50, 75, + 100, 125, 150, 175, + 200, 224, 249, 273, + 297, 321, 345, 369, + 392, 415, 438, 460, + 483, 505, 526, 548, + 569, 590, 610, 630, + 650, 669, 688, 706, + 724, 742, 759, 775, + 792, 807, 822, 837, + 851, 865, 878, 891, + 903, 915, 926, 936, + 946, 955, 964, 972, + 980, 987, 993, 999, + 1004, 1009, 1013, 1016, + 1019, 1021, 1023, 1024, + 1024, 1024, 1023, 1021, + 1019, 1016, 1013, 1009, + 1004, 999, 993, 987, + 980, 972, 964, 955, + 946, 936, 926, 915, + 903, 891, 878, 865, + 851, 837, 822, 807, + 792, 775, 759, 742, + 724, 706, 688, 669, + 650, 630, 610, 590, + 569, 548, 526, 505, + 483, 460, 438, 415, + 392, 369, 345, 321, + 297, 273, 249, 224, + 200, 175, 150, 125, + 100, 75, 50, 25, + 0, -25, -50, -75, + -100, -125, -150, -175, + -200, -224, -249, -273, + -297, -321, -345, -369, + -392, -415, -438, -460, + -483, -505, -526, -548, + -569, -590, -610, -630, + -650, -669, -688, -706, + -724, -742, -759, -775, + -792, -807, -822, -837, + -851, -865, -878, -891, + -903, -915, -926, -936, + -946, -955, -964, -972, + -980, -987, -993, -999, + -1004, -1009, -1013, -1016, + -1019, -1021, -1023, -1024, + -1024, -1024, -1023, -1021, + -1019, -1016, -1013, -1009, + -1004, -999, -993, -987, + -980, -972, -964, -955, + -946, -936, -926, -915, + -903, -891, -878, -865, + -851, -837, -822, -807, + -792, -775, -759, -742, + -724, -706, -688, -669, + -650, -630, -610, -590, + -569, -548, -526, -505, + -483, -460, -438, -415, + -392, -369, -345, -321, + -297, -273, -249, -224, + -200, -175, -150, -125, + -100, -75, -50, -25 +}; + +int fsqrt(int n) { + int lo, hi, mid, i, mm; + if (n <= 0) { return -1; } + lo = mini(1, n); + hi = maxi(1, n); + while ((100 << fbits * lo * lo) >> fbits * 2) + lo *= (10 << fbits) >> fbits; + while ((5 * hi * hi) >> fbits * 2 > n) + hi *= (1 << fbits) / 10; + for (i = 0; i < 100; i++) { + mid = ((lo + hi) * (1 << fbits)) / (2 << fbits); + mm = mid * mid >> fbits; + if (mm == n) return mid; + if (mm > n) hi = mid; + else lo = mid; + } + return mid; +} + +int absolute(int x) { + return x >= 0 ? x : -x; +} + +int fsin(int t) { + int index, sign; + sign = t >= 0 ? 1 : -1; + t = absolute(t); + index = (t * (sin_table_count) / ((1 << fbits) * 2)); + index = index % sin_table_count; + return (sign * sin_table[index]) / 2; +} + +int fcos(int t) { + return fsin((1 << fbits) / 2 - t); +} + +int ftan(int t) { + return (fsin(t) << fbits) / fcos(t); +} + +int flerp(int a, int b, int t) { + return a + ((t * (b - a)) >> fbits); +} + diff --git a/maths.h b/maths.h new file mode 100644 index 0000000..92861ba --- /dev/null +++ b/maths.h @@ -0,0 +1,17 @@ +#ifndef maths_h +#define maths_h + +#define mini(a_, b_) ((a_) < (b_) ? (a_) : (b_)) +#define maxi(a_, b_) ((a_) > (b_) ? (a_) : (b_)) +#define clamp(v_, min_, max_) (maxi(min_, mini(max_, v_))) + +#define fbits 9 + +int absolute(int x); +int fsin(int t); +int fcos(int t); +int ftan(int t); +int flerp(int a, int b, int t); +int fsqrt(int v); + +#endif diff --git a/memory.c b/memory.c new file mode 100644 index 0000000..2d7e566 --- /dev/null +++ b/memory.c @@ -0,0 +1,222 @@ +#include "memory.h" +#include "plat.h" +#include +#include + +int aligned(void* p, int a) { + return (uintptr_t)p % a == 0; +} + +int align_size(int s, int a) { + return (s + (a - 1)) & -a; +} + +static uintptr_t align_address( + uintptr_t ad, + size_t al +) { + size_t m; + m = al - 1; + return (ad + m) & ~m; +} + +void init_arena( + Arena* a, + void* mem, + int size +) { + a->buf = mem; + a->size = size; + a->ptr = 0; +} + +void clear_arena(Arena* a) { + a->ptr = 0; +} + +void* imp_arena_alloc( + Arena* a, + int size +) { + assert(a->ptr + size < a->size); + return &a->buf[a->ptr += size]; +} + +void* arena_alloc( + Arena* a, + int size +) { + return arena_alloc_aligned( + a, + size, + allocation_default_alignment + ); +} + +void* arena_alloc_aligned( + Arena* a, + int size, + int align +) { + void* p; + p = imp_arena_alloc( + a, + size + ); + return (void*)align_address((uintptr_t)p, align); +} + +void init_heap( + Heap* h, + void* mem, + int size +) { + int* fb; + assert(aligned(mem, 4)); + assert(size > 8); + h->buf = mem; + h->size = size; + h->blocks = 1; + fb = (int*)h->buf; + fb[0] = size; +} + +void* imp2_heap_alloc( + Heap* h, + int size +) { + int o, i; + int hs = sizeof(int); + int as = align_size(size + hs, hs); + int f = ~((unsigned)-1 >> 1); + for (i = o = 0; i < h->blocks; i++) { + int* phdr = (int*)&h->buf[o]; + int hdr = *phdr, bs; + assert(aligned(phdr, sizeof hdr)); + bs = hdr & ~f; + if (~hdr & f) { + if (as == bs) { + phdr[0] |= 1; + return phdr + 1; + } else { + int ns = bs - as; + if (ns > hs) { + int* nhdr = &phdr[ns / 4]; + phdr[0] = ns; + nhdr[0] = as | f; + h->blocks++; + return &nhdr[1]; + } + } + } else + o += bs; + } + return 0; +} + +void* imp_heap_alloc(Heap* h, int s) { + void* p = imp2_heap_alloc(h, s); + if (!p) { + heap_defrag(h); + p = imp2_heap_alloc(h, s); + } + return p; +} + +void imp_heap_free(Heap* h, void* p) { + assert((char*)p > h->buf); + assert((char*)p < h->buf + h->size); + (void)h; + ((int*)p)[-1] &= (unsigned)-1 >> 1; +} + +void* heap_alloc_aligned( + Heap* h, + int size, + int align +) { + unsigned char* p, * a; + ptrdiff_t shift; + size += (int)align; + p = imp_heap_alloc(h, size); + if (!p) { return 0; } + a = (unsigned char*)align_address((uintptr_t)p, align); + a += align * (unsigned)(p == a); + shift = a - p; + a[-1] = shift & 0xff; + return a; +} + +void heap_free_aligned(Heap* h, void* p) { + unsigned char* a; + ptrdiff_t shift; + a = p; + shift = a[-1]; + shift += 256 * shift == 0; + a -= shift; + imp_heap_free(h, a); +} + +void heap_defrag(Heap* h) { + int i, o, mtc; + int f = ~((unsigned)-1 >> 1); + for (i = o = mtc = 0; i < h->blocks; i++) { + int* phdr = (int*)&h->buf[o]; + int hdr = *phdr, bs, m, mc; + assert(aligned(phdr, sizeof hdr)); + bs = hdr & ~f; + if (~hdr & f) { + for ( + m = bs, mc = 0, i++; + i < h->blocks; + i++, mc++ + ) { + int mhdr = *(int*)&h->buf[o + m]; + if (~mhdr & f) + m += mhdr & ~f; + else + break; + } + i--; + bs = m; + phdr[0] = bs; + mtc += mc; + } + o += bs; + } + h->blocks -= mtc; +} + +void* heap_alloc( + Heap* h, + int size +) { + return heap_alloc_aligned( + h, + size, + allocation_default_alignment + ); +} + +void heap_free( + Heap* h, + void* p +) { + heap_free_aligned(h, p); +} + +/* +void print_blocks(Heap* h) { + int i, o; + int fb = ~((unsigned)-1 >> 1); + for (i = o = 0; i < h->blocks; i++) { + int b = *(int*)&h->buf[o]; + int bs = b & ~fb; + int f = ~b & fb; + printf("%s %d\n", f? "free": " ", bs); + o += bs; + } + assert(o == h->size); +} +*/ + diff --git a/memory.h b/memory.h new file mode 100644 index 0000000..1dc5021 --- /dev/null +++ b/memory.h @@ -0,0 +1,56 @@ +#ifndef memory_h +#define memory_h + +int aligned(void* p, int a); +int align_size(int s, int a); + +typedef struct { + char* buf; + int size, ptr; +} Arena; + +void init_arena( + Arena* a, + void* mem, + int size +); +void clear_arena(Arena* a); +void* arena_alloc( + Arena* a, + int size +); +void* arena_alloc_aligned( + Arena* a, + int size, + int align +); + +typedef struct { + char* buf; + int size, blocks; +} Heap; + +void init_heap( + Heap* h, + void* mem, + int size +); +void* heap_alloc( + Heap* h, + int size +); +void* heap_alloc_aligned( + Heap* h, + int size, + int align +); +void heap_free_aligned( + Heap* h, + void* p +); +void heap_free( + Heap* h, + void* p +); +void heap_defrag(Heap* h); +#endif diff --git a/plat.c b/plat.c new file mode 100644 index 0000000..e0d72be --- /dev/null +++ b/plat.c @@ -0,0 +1,413 @@ +#include "config.h" +#include "error.h" +#include "memory.h" +#include "plat.h" + +#ifdef plat_posix + +#define _POSIX_SOURCE +#define _GNU_SOURCE +#include +#include +#include +#include +#include + +extern int isatty(int); +extern int fileno(FILE*); + +int imp_assert( + int val, + const char* expr, + const char* file, + int line +) { + if (!val) { + print_err( + "%d:%s: Assertion failed: %s.\n", + line, + file, + expr + ); + pbreak(error_assertion_failed); + return 0; + } + return 1; +} + +void print(const char* fmt, ...) { + va_list args; + va_start(args, fmt); + vfprintf(stdout, fmt, args); + va_end(args); +} + +void print_err(const char* fmt, ...) { + va_list args; + va_start(args, fmt); + + if (isatty(fileno(stderr))) { + fprintf(stderr, "\033[31;31m"); + } + + vfprintf(stderr, fmt, args); + + if (isatty(fileno(stderr))) { + fprintf(stderr, "\033[0m"); + } + + va_end(args); +} + +void print_war(const char* fmt, ...) { + va_list args; + va_start(args, fmt); + + if (isatty(fileno(stderr))) { + fprintf(stderr, "\033[31;35m"); + } + + vfprintf(stderr, fmt, args); + + if (isatty(fileno(stderr))) { + fprintf(stderr, "\033[0m"); + } + + va_end(args); +} + +void pbreak(Error code) { +#if defined(DEBUG) && defined(plat_x86) + __asm__("int3;"); + (void)code; +#else + exit(code); +#endif +} + +static clockid_t global_clock; +static unsigned long global_freq; + +void init_timer(void) { + global_clock = CLOCK_REALTIME; + global_freq = 1000000000; +#if defined(_POSIX_MONOTONIC_CLOCK) + if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) { + global_clock = CLOCK_MONOTONIC; + } +#endif +} + +unsigned long get_timer(void) { + struct timespec ts; + clock_gettime(global_clock, &ts); + return + (unsigned long)ts.tv_sec * global_freq + + (unsigned long)ts.tv_nsec; +} + +void sleep_ns(unsigned long ns) { + struct timespec t = { 0 }; + t.tv_nsec = ns; + nanosleep(&t, &t); +} + +void init_fps(FPS* f, int mpf) { + f->now = get_timer(); + f->next = f->now; + f->mpf = mpf; + f->pt = 0; + f->ct = -1; +} + +void fps_begin(FPS* f) { + f->now = get_timer(); +} + +void fps_end(FPS* f) { + unsigned long ts = f->next - f->now; + if (ts > 0) { + sleep_ns(ts); + } +} + +void fps_update(FPS* f) { + f->next += f->mpf * 1000000; + f->pt = f->ct; + f->ct = get_timer(); + f->fps = 1000000000 / (f->ct - f->pt); +} + +#include + +extern int entrypoint(int, const char**, Arena*); + +int main(int argc, const char** argv) { + Arena a; + void* mem = malloc(memory_size); + if (!mem) { + print_err("Out of memory.\n"); + return error_out_of_memory; + } + init_timer(); + init_arena( + &a, + mem, + memory_size + ); + return entrypoint(argc, argv, &a); +} + +#endif + +#ifdef plat_x11 + +#include +#include + +#include + +typedef struct { + Display* d; + Window wi; + GC gc; + int w, h; + unsigned long begin, end; + XImage* bb; + Colour* bbp; + Atom wm_delete; +} App_Internal; + +void init_rendering( + App* a, + App_Internal* i, + XWindowAttributes* wa +) { + Colour* p, * fb; + int w = i->w = wa->width; + int h = i->h = wa->height; + int tw = (w + a->s) / a->s; + int th = (h + a->s) / a->s; + a->w = tw; + a->h = th; + if (i->bb) { + heap_free(a->heap, a->fb); + XDestroyImage(i->bb); + } + p = malloc(sizeof *p * w * h); + fb = heap_alloc(a->heap, sizeof *p * tw * th); + if (!p || !fb) { + print_err("Out of memory.\n"); + pbreak(error_out_of_memory); + } + a->fb = fb; + i->bb = XCreateImage( + i->d, + wa->visual, + wa->depth, + ZPixmap, + 0, + (char*)p, + w, + h, + 32, + w * sizeof *p + ); + if (!i->bb) { + print_err("Failed to create X11 backbuffer.\n"); + pbreak(error_platform_error); + } + i->bbp = p; +} + +void init_window(App* a, App_Internal* i, const char* n) { + Window root, w; + Display* d; + XWindowAttributes wa; + unsigned rm, bm; + i->bb = 0; + i->bbp = 0; + d = i->d; + root = DefaultRootWindow(d); + w = XCreateSimpleWindow( + d, + root, + 0, + 0, + a->w * a->s, + a->h * a->s, + 0, + WhitePixel(d, 0), + BlackPixel(d, 0) + ); + i->wi = w; + i->wm_delete = XInternAtom( + d, + "WM_DELETE_WINDOW", + 0 + ); + XSetWMProtocols(d, w, &i->wm_delete, 1); + XStoreName(d, w, n); + XSelectInput( + d, + w, + ExposureMask | + KeyPressMask | + KeyReleaseMask | + PointerMotionMask | + ButtonPressMask | + ButtonReleaseMask + ); + XMapRaised(d, w); + i->gc = XCreateGC(d, w, 0, 0); + if (!i->gc) { + print_err("Failed to create X graphics context.\n"); + pbreak(error_platform_error); + } + XGetWindowAttributes(d, w, &wa); + if (wa.depth != 24 && wa.depth != 32) { + print_err("Only true colour displays are supported.\n"); + pbreak(error_platform_error); + } + rm = wa.visual->red_mask & 0x1000000; + bm = wa.visual->blue_mask & 0xffffff; + if ((rm == 0xff && bm == 0xff0000)) { + print_war("Detected BGR. Colours will look fucked.\n"); + } + init_rendering(a, i, &wa); +} + +App* new_app(Heap* mem, int w, int h, const char* n) { + App* a; + App_Internal* i; + a = heap_alloc(mem, sizeof *a + sizeof *i); + i = (App_Internal*)(&a[1]); + a->heap = mem; + a->s = 2; + a->o = 0; + a->err = error_none; + a->w = w; + a->h = h; + i->d = XOpenDisplay(0); + if (!i->d) { + print_err("Failed to open X11 display.\n"); + pbreak(error_platform_error); + } + init_window(a, i, n); + a->o = 1; + return a; +} + +void deinit_app(App* a) { + App_Internal* i = (App_Internal*)(&a[1]); + XDestroyImage(i->bb); + XFreeGC(i->d, i->gc); + XDestroyWindow(i->d, i->wi); + XCloseDisplay(i->d); + heap_free(a->heap, a); +} + +void app_begin(App* a) { + App_Internal* i = (App_Internal*)(&a[1]); + Display* d = i->d; + Window w = i->wi; + i->begin = get_timer(); + while (XPending(d)) { + XEvent e; + XWindowAttributes wa; + int nw, nh; + XNextEvent(d, &e); + switch (e.type) { + case ClientMessage: + if ( + (Atom)e.xclient.data.l[0] == + i->wm_delete + ) { + a->o = 0; + } + break; + case Expose: + XGetWindowAttributes(d, w, &wa); + nw = wa.width; + nh = wa.height; + if ( + !i->bb || + nw != i->w || + nh != i->h + ) { + init_rendering(a, i, &wa); + } + break; + case MotionNotify: + a->mx = e.xmotion.x / a->s; + a->my = e.xmotion.y / a->s; + break; + default: + break; + } + } +} + +void app_rencpy( + App* a, + App_Internal* i, + int x, + int y, + int w, + int h +) { + int rat_x, rat_y; + int rrat_x, rrat_y; + int sx, sy; + int ix, iy, sw, sh, dw, dh; + int dsx, dsy; + int dex, dey; + int pidx, fbits = 9; + Colour* dst; + const Colour* src; + unsigned char t; + sw = a->w; + sh = a->h; + dw = i->w; + dh = i->h; + rat_x = (sw << fbits * 2) / (dw << fbits); + rat_y = (sh << fbits * 2) / (dh << fbits); + rrat_x = (dw << fbits * 2) / (sw << fbits); + rrat_y = (dh << fbits * 2) / (sh << fbits); + dsx = (x * rrat_x) >> fbits; + dsy = (y * rrat_y) >> fbits; + dex = ((x + w) * rrat_x) >> fbits; + dey = ((y + h) * rrat_y) >> fbits; + dst = i->bbp; + src = a->fb; + for (iy = dsy; iy < dey; iy++) { + sy = (rat_y * (iy << fbits)) >> (fbits * 2); + for (ix = dsx; ix < dex; ix++) { + sx = (rat_x * (ix << fbits)) >> (fbits * 2); + pidx = sx + sy * sw; + dst = &i->bbp[ix + iy * dw]; + *dst = src[pidx]; + t = dst->r; + dst->r = dst->b; + dst->b = t; + } + } + XPutImage( + i->d, + i->wi, + i->gc, + i->bb, + dsx, dsy, + dsx, dsy, + dex - dsx, dey - dsy + ); +} + +void app_end(App* a) { + App_Internal* i = (App_Internal*)(&a[1]); + app_rencpy(a, i, 0, 0, a->w, a->h); + i->end = get_timer(); + a->fps = 1000000000 / (i->end - i->begin); +} + +#endif diff --git a/plat.h b/plat.h new file mode 100644 index 0000000..7e4f445 --- /dev/null +++ b/plat.h @@ -0,0 +1,58 @@ +#ifndef plat_h +#define plat_h + +#include "error.h" +#include "memory.h" +#include "render.h" + +#ifdef assert +#undef assert +#endif + +#define assert(expr) \ + imp_assert( \ + expr, \ + #expr, \ + __FILE__, \ + __LINE__ \ + ) + +int imp_assert( + int val, + const char* expr, + const char* file, + int line +); + +void print(const char* fmt, ...); +void print_err(const char* fmt, ...); +void print_war(const char* fmt, ...); +void pbreak(Error code); + +typedef struct { + int mpf; + int fps; + unsigned long pt, ct; + unsigned long now, next; +} FPS; + +void init_fps(FPS* f, int mpf); +void fps_begin(FPS* f); +void fps_end(FPS* f); +void fps_update(FPS* f); + +typedef struct { + int w, h, s, o, mpf; + int fps; + int mx, my; + Error err; + Colour* fb; + Heap* heap; +} App; + +App* new_app(Heap* mem, int w, int h, const char* n); +void deinit_app(App* a); +void app_begin(App* a); +void app_end(App* a); + +#endif diff --git a/rect.c b/rect.c new file mode 100644 index 0000000..c72c66e --- /dev/null +++ b/rect.c @@ -0,0 +1,46 @@ +#include "maths.h" +#include "rect.h" + +Rect* rect_clip(Rect* r, const Rect* c) { + int n; + int x = r->x, y = r->y; + if ((n = c->x - r->x) > 0) { + r->w -= n; + r->x += n; + } + if ((n = c->y - r->y) > 0) { + r->h -= n; + r->y += n; + } + if ((n = x + r->w - (c->x + c->w)) > 0) + r->w -= n; + if ((n = y + r->h - (c->y + c->h)) > 0) + r->h -= n; + return r; +} + +Rect* rect_clips(Rect* r, Rect* s, const Rect* c) { + int n; + int x = r->x, y = r->y; + if ((n = c->x - r->x) > 0) { + r->w -= n; + r->x += n; + s->w -= n; + s->x += n; + } + if ((n = c->y - r->y) > 0) { + r->h -= n; + r->y += n; + s->h -= n; + s->y += n; + } + if ((n = x + r->w - (c->x + c->w)) > 0) { + r->w -= n; + s->w -= n; + } + if ((n = y + r->h - (c->y + c->h)) > 0) { + r->h -= n; + s->h -= n; + } + return r; +} diff --git a/rect.h b/rect.h new file mode 100644 index 0000000..b055607 --- /dev/null +++ b/rect.h @@ -0,0 +1,11 @@ +#ifndef rect_h +#define rect_h + +typedef struct { + int x, y, w, h; +} Rect; + +Rect* rect_clip(Rect* r, const Rect* c); +Rect* rect_clips(Rect* r, Rect* s, const Rect* c); + +#endif diff --git a/render.c b/render.c new file mode 100644 index 0000000..74da997 --- /dev/null +++ b/render.c @@ -0,0 +1,207 @@ +#include "render.h" + +static int font_w = 960; +static unsigned font_data[] = { + 0x00000000, 0x00003000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x0cc0c000, 0x8000301b, 0x60601803, 0x00000000, 0x20000000, + 0x0780c078, 0x83f0e01e, 0xe0783f07, 0x60000001, 0x1e018000, + 0x07e0c078, 0xe3f87e1f, 0xe0cc1e0f, 0x1e638f01, 0x1f0c6318, + 0x07e1f07e, 0xc330fc1e, 0x30cc318c, 0x021e0fc3, 0x000101e0, + 0x00e00000, 0x0000e000, 0xc00e0007, 0x3c038c00, 0x00000000, + 0x00000000, 0x00001000, 0x00000000, 0x30380000, 0x0000c070, + 0x0cc1e000, 0xc630f81b, 0xc0301806, 0x00000cc0, 0x30000000, + 0x0cc0f0cc, 0xc030f033, 0x30cc330c, 0x30000003, 0x33030000, + 0x8cc1e0cc, 0xc230cc33, 0xc0cc3308, 0x0c630600, 0x318ce3b8, + 0x0cc318cc, 0xc330b433, 0x30cc318c, 0x06060cc3, 0x00038180, + 0x00c00018, 0x8000c000, 0x000c000d, 0x30030000, 0x00000000, + 0x00000000, 0x00001800, 0x00000000, 0x300c0000, 0x0409e0c0, + 0x8481e000, 0xc3300c3f, 0x80180c06, 0x000c0781, 0x18000000, + 0x0c00c0ec, 0xc1f0d830, 0x30cc1800, 0x180c0303, 0x30060000, + 0x8cc330ec, 0xc0318c01, 0xc0cc3180, 0x0c330600, 0x318de3f8, + 0x0cc318cc, 0xc3303003, 0x3078318c, 0x0c060643, 0x0006c180, + 0x07c0f018, 0x81e0f81e, 0xf06c3701, 0x30330f00, 0x1e07c1f8, + 0x0ee37076, 0xc330fc3e, 0x30c6318c, 0x300c0fc3, 0x0e0f20c0, + 0x0001e000, 0x8180781b, 0x80180013, 0x000c1fe1, 0x0c000000, + 0x0600c0dc, 0xc300cc1c, 0xe0780c07, 0x0c0c0303, 0x180c03f0, + 0x87c330ec, 0xc1f18c01, 0xc0fc0187, 0x0c1f0600, 0x318fe358, + 0x07c3187c, 0xc330300e, 0xe030358c, 0x18060301, 0x000c6180, + 0x0cc18030, 0xc330cc33, 0xc0dc1987, 0x301b0c00, 0x330cc358, + 0x0dc198cc, 0xc3301803, 0x306c358c, 0x00060643, 0x1b060180, + 0x0000c000, 0xc0c0c01b, 0x8018001e, 0x003f0781, 0x060003f0, + 0x0300c0cc, 0xc301fc30, 0x00cc0c0c, 0x0c000003, 0x0c0c0000, + 0x8cc3f00c, 0xc0318c01, 0xc0cc3980, 0x0c330660, 0x318f6318, + 0x06c3980c, 0xc3303018, 0xc078358c, 0x30060180, 0x00000180, + 0x0cc1f000, 0x83f0cc03, 0xc0cc1981, 0x301f0c00, 0x330cc358, + 0x00c198cc, 0xc330181e, 0x3038358c, 0x300c0303, 0x318000c0, + 0x80000000, 0xc6607c3f, 0xc030000c, 0x300c0cc0, 0x03030000, + 0x0180c0cc, 0xc330c033, 0x30cc0c0c, 0x180c0303, 0x000603f0, + 0x8cc3308c, 0xc230cc33, 0xc0cc3300, 0x8c630660, 0x318e6318, + 0x0cc1f00c, 0x83303033, 0xc0cc1f07, 0x600609c0, 0x00000180, + 0x0cc19800, 0x8030cc33, 0xc0cc1f01, 0x30330cc0, 0x330cc358, + 0x00c198cc, 0x8330d830, 0xe06c1f07, 0x300c0981, 0x318000c0, + 0x0000c000, 0x8630301b, 0x6060001b, 0x300c0000, 0x01830000, + 0x0fc3f078, 0x81e0c01e, 0xe0780c07, 0x300c0301, 0x0c030000, + 0x07e33078, 0xe3f87e1f, 0xe0cc3e01, 0xfe6383c1, 0x1f0c6318, + 0x0ce3c01e, 0x01e0781e, 0xe0cc1b03, 0xc01e0fc1, 0x000001e0, + 0x07e3f000, 0xc1e0f81e, 0xf0ce1803, 0xfc338783, 0x1e0cc318, + 0x01e1f07c, 0x06e0701f, 0x80c61b03, 0x30380fc1, 0x3f800070, + 0x0000c000, 0x0000301b, 0x00000000, 0x18000000, 0x00800000, + 0x00000000, 0x00000000, 0x00000000, 0x60060000, 0x00018000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x80000000, 0x7f800000, + 0x00000000, 0x00000000, 0x00000f00, 0x00000000, 0x00000000, + 0x0001800c, 0x00000000, 0xf0000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x0003c01e, 0x00000000, 0x00000000, 0x00000000, 0x00000000 +}; + +Colour make_colour(unsigned rgb, unsigned char a) { + Colour r; + r.r = (unsigned char)(rgb >> 16); + r.g = (unsigned char)(rgb >> 8); + r.b = (unsigned char)rgb; + r.a = a; + return r; +} + +Colour make_red(void) { + return make_colour(0xff0000, 0xff); +} + +Colour make_green(void) { + return make_colour(0x00ff00, 0xff); +} + +Colour make_blue(void) { + return make_colour(0x0000ff, 0xff); +} + +Colour make_cyan(void) { + return make_colour(0x00ffff, 0xff); +} + +Colour make_pink(void) { + return make_colour(0xff00ff, 0xff); +} + +Colour make_yellow(void) { + return make_colour(0xffff00, 0xff); +} + +Colour make_aliceblue(void) { + return make_colour(0xf0f8ff, 0xff); +} + +Colour blend(Colour dst, Colour src) { + int ima; + ima = 0xff - src.a; + dst.r = (unsigned char)(((src.r * src.a) + (dst.r * ima)) >> 8); + dst.g = (unsigned char)(((src.g * src.a) + (dst.g * ima)) >> 8); + dst.b = (unsigned char)(((src.b * src.a) + (dst.b * ima)) >> 8); + return dst; +} + +Colour blend_mod(Colour dst, Colour src, Colour mod) { + int ima; + src.a = (src.a * mod.a) >> 8; + ima = 0xff - src.a; + dst.r = (unsigned char)(((src.r * mod.r * src.a) >> 16) + ((dst.r * ima) >> 8)); + dst.g = (unsigned char)(((src.g * mod.g * src.a) >> 16) + ((dst.g * ima) >> 8)); + dst.b = (unsigned char)(((src.b * mod.b * src.a) >> 16) + ((dst.b * ima) >> 8)); + return dst; +} + + +void ren_begin(Renderer* r, Colour* t, int w, int h) { + r->t = t; + r->w = w; + r->h = h; + r->clip.x = 0; + r->clip.y = 0; + r->clip.w = w; + r->clip.h = h; +} + +void ren_end(Renderer* r) { + (void)r; +} + +void ren_clear(Renderer* r) { + Colour* d = r->t, b = { 0 }; + int i = 0, e = r->w * r->h; + for (i = 0; i < e; i++, d++) + *d = b; +} + +void ren_clearc(Renderer* r, Colour c) { + Colour* d = r->t; + int i = 0, e = r->w * r->h; + for (i = 0; i < e; i++, d++) + *d = c; +} + +void ren_clip(Renderer* r, const Rect* c) { + r->clip = *c; +} + +void ren_point(Renderer* r, Colour c, int x, int y) { + if (x < 0) return; + if (y < 0) return; + if (x >= r->w) return; + if (y >= r->h) return; + r->t[x + y * r->w] = c; +} + +void ren_char( + Renderer* r, + Colour c, + int x, + int y, + char ch +) { + int i, j, k, l, ex, ey, s; + Colour* dst; + Rect re = { 0, 0, 10, 10 }; + Rect sub = { 0, 0, 10, 10 }; + re.x = x; + re.y = y; + sub.x = (ch - ' ') * 10; + rect_clips(&re, &sub, &r->clip); + ex = re.x + re.w; + ey = re.y + re.h; + dst = r->t + (re.x + re.y * r->w); + s = r->w - re.w; + for (j = re.y, l = sub.y; j < ey; j++, l++) { + for (i = re.x, k = sub.x; i < ex; i++, k++) { + int si = k + l * font_w; + unsigned bits = font_data[si / 32]; + int bit = bits & (1 << si % 32); + if (bit) + *dst = blend(*dst, c); + dst++; + } + dst += s; + } +} + +void ren_text( + Renderer* r, + Colour c, + int x, + int y, + const char* t +) { + const char* s; + for (s = t; *s; s++, x += 10) + ren_char(r, c, x, y, *s); +} + diff --git a/render.h b/render.h new file mode 100644 index 0000000..36bafe4 --- /dev/null +++ b/render.h @@ -0,0 +1,48 @@ +#ifndef render_h +#define render_h + +#include "rect.h" + +typedef struct { + unsigned char r, g, b, a; +} Colour; + +Colour make_colour(unsigned rgb, unsigned char a); +Colour make_red(void); +Colour make_green(void); +Colour make_blue(void); +Colour make_cyan(void); +Colour make_pink(void); +Colour make_yellow(void); +Colour make_aliceblue(void); +Colour blend(Colour dst, Colour src); +Colour blend_mod(Colour dst, Colour src, Colour mod); + +typedef struct { + Colour* t; + int w, h; + Rect clip; +} Renderer; + +void ren_begin(Renderer* r, Colour* t, int w, int h); +void ren_end(Renderer* r); +void ren_clear(Renderer* r); +void ren_clearc(Renderer* r, Colour c); +void ren_clip(Renderer* r, const Rect* c); +void ren_point(Renderer* r, Colour c, int x, int y); +void ren_char( + Renderer* r, + Colour c, + int x, + int y, + char ch +); +void ren_text( + Renderer* r, + Colour c, + int x, + int y, + const char* t +); + +#endif -- cgit v1.2.3-54-g00ecf