diff options
author | Charlie Stanton <charlie@shtanton.xyz> | 2025-04-05 08:10:21 +0100 |
---|---|---|
committer | Charlie Stanton <charlie@shtanton.xyz> | 2025-04-05 08:10:21 +0100 |
commit | 935f69ca25bca24fd36166e7192ebb69af2d35e9 (patch) | |
tree | 182cd864dc0d486ca64868f037da306b60ec51e2 | |
parent | f0bf72a54477fb18b1666293a652b52f53320a1d (diff) | |
download | ldjam57-935f69ca25bca24fd36166e7192ebb69af2d35e9.tar |
Get web assembly working
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | Makefile | 19 | ||||
-rw-r--r-- | src/all.c | 83 | ||||
-rw-r--r-- | src/all.h | 13 | ||||
-rw-r--r-- | src/index.html.in | 105 |
5 files changed, 185 insertions, 36 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..378eac2 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +build @@ -1,2 +1,17 @@ -main: src/all.c src/all.h - gcc -Wall -Wextra -Wpedantic -DSDL $$(pkg-config --libs sdl3) -o main src/all.c +build/main: src/all.c src/all.h + mkdir -p build + gcc -Wall -Wextra -Wpedantic -DSDL $$(pkg-config --libs sdl3) -o build/main src/all.c + +build/main.wasm: src/all.c src/all.h + mkdir -p build + clang --target=wasm32 -nostdlib -DWASM -Wall -Wextra -Wpedantic \ + -Wl,--no-entry -fno-builtin -o build/main.wasm src/all.c + +build/main.wasm.b64: build/main.wasm + (printf '"'; base64 < build/main.wasm | tr -d '\n'; printf '"') > build/main.wasm.b64 + +build/index.html: src/index.html.in build/main.wasm.b64 + clang -E -P -undef -nostdinc -x c -o build/index.html src/index.html.in + +clean: + rm -r build @@ -1,4 +1,15 @@ -#include "all.h" +#include <stddef.h> +#include <stdint.h> + +typedef struct { + int x, y, w, h; + unsigned char r, g, b, a; +} DrawElement; + +typedef struct { + int len; + DrawElement els[256]; +} DrawList; #define MEM_SIZE (1<<16) @@ -30,6 +41,7 @@ typedef struct { int x, y; } State; +// Mirror these in src/index.html.in enum { INPUT_NONE, INPUT_UP, @@ -50,18 +62,14 @@ static DrawList *render(State state, UI *ui, Arena *a) { DrawList *drawList = new(a, 1, DrawList); drawList->len = 1; drawList->els[0] = (DrawElement) { - .rect = (SDL_Rect) { - .x = state.x, - .y = state.y, - .w = 100, - .h = 100, - }, - .color = (SDL_Color) { - .r = 255, - .g = 0, - .b = 0, - .a = 255, - }, + .x = state.x, + .y = state.y, + .w = 100, + .h = 100, + .r = 255, + .g = 0, + .b = 0, + .a = 255, }; return drawList; @@ -88,6 +96,8 @@ static void update(Game *game, Arena a) { #if SDL +#include <SDL3/SDL.h> + int main(int argc, char **argv) { (void) argc; (void) argv; @@ -154,16 +164,16 @@ int main(int argc, char **argv) { for (int i = 0; i < drawList->len; i++) { SDL_SetRenderDrawColor( r, - drawList->els[i].color.r, - drawList->els[i].color.g, - drawList->els[i].color.b, - drawList->els[i].color.a + drawList->els[i].r, + drawList->els[i].g, + drawList->els[i].b, + drawList->els[i].a ); SDL_FRect rect = { - .x = (float) drawList->els[i].rect.x, - .y = (float) drawList->els[i].rect.y, - .w = (float) drawList->els[i].rect.w, - .h = (float) drawList->els[i].rect.h + .x = (float) drawList->els[i].x, + .y = (float) drawList->els[i].y, + .w = (float) drawList->els[i].w, + .h = (float) drawList->els[i].h }; SDL_RenderFillRect(r, &rect); } @@ -177,4 +187,35 @@ int main(int argc, char **argv) { } #elif WASM + +static Game *game; +static Arena perm; + +__attribute((export_name("game_init"))) +void game_init(void) { + static char heap[MEM_SIZE]; + perm.start = heap; + perm.end = heap + MEM_SIZE; + + game = new(&perm, 1, Game); + game->state = (State) { + .x = 100, + .y = 100, + }; +} + +__attribute((export_name("game_render"))) +DrawList *game_render(int width, int height) { + game->ui.width = width; + game->ui.height = height; + Arena scratch = perm; + return render(game->state, &game->ui, &scratch); +} + +__attribute((export_name("game_update"))) +void game_update(int input) { + game->input = input; + update(game, perm); +} + #endif @@ -1,13 +0,0 @@ -#include <SDL3/SDL.h> -#include <stddef.h> -#include <stdint.h> - -typedef struct { - SDL_Rect rect; - SDL_Color color; -} DrawElement; - -typedef struct { - int len; - DrawElement els[256]; -} DrawList; diff --git a/src/index.html.in b/src/index.html.in new file mode 100644 index 0000000..fcfb8ef --- /dev/null +++ b/src/index.html.in @@ -0,0 +1,105 @@ +<!doctype html> +<title>LDJam 57</title> +<meta charset="utf-8"/> +<meta name="viewport" content="width=device-width"> +<style> +html, body { + background: #222222; + font-family: sans-serif; + height: 100%; + margin: 0; + overflow: hidden; + text-align: center; + width: 100%; +} +h1 { + color: white; + margin: 0; +} +button { + font-size: 150%; + height: 8%; + margin: 0.3em; + width: 25%; +} +</style> + +<canvas></canvas> + +<script> +// Mirror these in src/all.c +const INPUT_NONE = 0; +const INPUT_UP = 1; +const INPUT_DOWN = 2; +const INPUT_LEFT = 3; +const INPUT_RIGHT = 4; + +const WASM = +#include "../build/main.wasm.b64" + +async function main() { + let bytes = Uint8Array.from(atob(WASM), function(c) { + return c.charCodeAt(0); + }); + let module = await WebAssembly.compile(bytes); + let wasm = await WebAssembly.instantiate(module); + let exports = wasm.exports; + let html = document.querySelector("html"); + let canvas = document.querySelector("canvas"); + let ctx = canvas.getContext("2d"); + let memory = exports.memory; + + function min(a, b) { + return b<a ? b : a; + } + + function max_width() { + return html.clientHeight * 0.7 | 0; + } + + function render() { + let width = canvas.width = min(html.clientWidth, max_width()); + let height = canvas.height = width; + let ptr = exports.game_render(width, height); + let dl = new Int32Array(memory.buffer, ptr); + let len = dl[0]; + let ops = dl.subarray(1); + + for (let i = 0; i < len; i++) { + let op = ops.subarray(5*i, 5*i+5); + const color = new Uint8Array(new Uint32Array(ops.subarray(4, 5)).buffer); + let style = `#${color[0].toString(16).padStart(2, "0")}${color[1].toString(16).padStart(2, "0")}${color[2].toString(16).padStart(2, "0")}`; + ctx.fillStyle = style; + ctx.fillRect(op[0], op[1], op[2], op[3]); + } + } + + function onresize() { html.style.maxWidth = `${max_width()}px`; } + window.addEventListener("resize", onresize); + onresize(); + + document.addEventListener("keydown", function(e) { + if (e.key == "w") { + exports.game_update(INPUT_UP); + } else if (e.key == "a") { + exports.game_update(INPUT_LEFT); + } else if (e.key == "s") { + exports.game_update(INPUT_DOWN); + } else if (e.key == "d") { + exports.game_update(INPUT_RIGHT); + } + }) + + function animate() { + // TODO: stop requesting frames when state is static + requestAnimationFrame(animate); + exports.game_update(INPUT_NONE); + render(); + } + requestAnimationFrame(animate); + + exports.game_init(); +} + +main() +</script> |