From eefa0d0836f026606c00b4c9e7f259c6147251f9 Mon Sep 17 00:00:00 2001 From: Brendan Hansen Date: Tue, 23 Nov 2021 12:25:52 -0600 Subject: [PATCH] added hb.timer; starting snake example --- Makefile | 6 +-- build.bat | 2 +- include/heartbreak.h | 1 + misc/onyx/heartbreak.onyx | 5 +- misc/onyx/heartbreak_thread.onyx | 4 -- misc/onyx/heartbreak_timer.onyx | 15 ++++++ src/gfx.c | 2 +- src/heartbreak.c | 1 + src/heartbreak_thread.c | 15 ------ src/heartbreak_timer.c | 74 ++++++++++++++++++++++++++ tests/simp.onyx | 48 ++++++++++++++--- tests/snake.onyx | 89 ++++++++++++++++++++++++++++++++ 12 files changed, 230 insertions(+), 32 deletions(-) delete mode 100644 misc/onyx/heartbreak_thread.onyx create mode 100644 misc/onyx/heartbreak_timer.onyx create mode 100644 src/heartbreak_timer.c create mode 100644 tests/snake.onyx diff --git a/Makefile b/Makefile index 7107978..80f79e4 100644 --- a/Makefile +++ b/Makefile @@ -6,7 +6,7 @@ INCLUDES=-I./include -I./lib/linux_x86_64/include -I./lib/common/include LIBS=-L./lib/linux_x86_64/lib -lwasmer -Wl,-rpath=./lib/linux_x86_64/lib -lglfw -lGL -lm -lpthread TARGET=./bin/heartbreak -OBJECT_FILES=./build/heartbreak.o ./build/utils.o ./build/gfx.o ./build/heartbreak_system.o ./build/heartbreak_graphics.o ./build/heartbreak_input.o ./build/heartbreak_window.o ./build/heartbreak_thread.o +OBJECT_FILES=./build/heartbreak.o ./build/utils.o ./build/gfx.o ./build/heartbreak_system.o ./build/heartbreak_graphics.o ./build/heartbreak_input.o ./build/heartbreak_window.o ./build/heartbreak_thread.o ./build/heartbreak_timer.o build/%.o: src/%.c $(CC) $(FLAGS) $(INCLUDES) -o $@ $(LIBS) -c $< @@ -20,6 +20,6 @@ clean: $(TARGET): $(OBJECT_FILES) $(CC) $(FLAGS) -o $@ $^ $(LIBS) -all: $(TARGET) tests/minesweeper.wasm tests/simp.wasm +all: $(TARGET) tests/minesweeper.wasm tests/simp.wasm tests/snake.wasm run: all - ./bin/heartbreak tests/simp.wasm + ./bin/heartbreak tests/snake.wasm diff --git a/build.bat b/build.bat index da78133..61d8151 100644 --- a/build.bat +++ b/build.bat @@ -1,7 +1,7 @@ @echo off set FLAGS=/O2 /MT /Z7 /TC /std:c17 -set SOURCE=src/gfx.c src/heartbreak.c src/heartbreak_graphics.c src/heartbreak_input.c src/heartbreak_system.c src/heartbreak_thread.c src/heartbreak_window.c src/utils.c +set SOURCE=src/gfx.c src/heartbreak.c src/heartbreak_graphics.c src/heartbreak_input.c src/heartbreak_system.c src/heartbreak_thread.c src/heartbreak_timer.c src/heartbreak_window.c src/utils.c set LIBS=lib\windows_x86_64\lib\wasmer.lib C:\tools\glfw-3.3.5.bin.WIN64\lib-vc2019\glfw3.lib C:\tools\glfw-3.3.5.bin.WIN64\lib-vc2019\glfw3dll.lib opengl32.lib ws2_32.lib Advapi32.lib userenv.lib bcrypt.lib set LINKFLAGS=/incremental:no /opt:ref /subsystem:console set OUT=bin\heartbreak.exe diff --git a/include/heartbreak.h b/include/heartbreak.h index 5de3d3f..05a70c8 100644 --- a/include/heartbreak.h +++ b/include/heartbreak.h @@ -76,6 +76,7 @@ extern WasmFuncDefinition* __heartbreak_module_system[]; extern WasmFuncDefinition* __heartbreak_module_graphics[]; extern WasmFuncDefinition* __heartbreak_module_input[]; extern WasmFuncDefinition* __heartbreak_module_thread[]; +extern WasmFuncDefinition* __heartbreak_module_timer[]; extern WasmFuncDefinition* __heartbreak_module_window[]; #endif diff --git a/misc/onyx/heartbreak.onyx b/misc/onyx/heartbreak.onyx index eabaf95..0e0d62a 100644 --- a/misc/onyx/heartbreak.onyx +++ b/misc/onyx/heartbreak.onyx @@ -4,7 +4,7 @@ package heartbreak #load "./heartbreak_system" #load "./heartbreak_input" #load "./heartbreak_thread_runtime" -#load "./heartbreak_thread" +#load "./heartbreak_timer" #load "./heartbreak_window" use package core {println} @@ -24,7 +24,8 @@ run :: (use funcs: HeartbreakFuncs) { load(); while system.end_frame() { - update(0); + dt := timer.step(); + update(dt); draw(); } diff --git a/misc/onyx/heartbreak_thread.onyx b/misc/onyx/heartbreak_thread.onyx deleted file mode 100644 index c35e42d..0000000 --- a/misc/onyx/heartbreak_thread.onyx +++ /dev/null @@ -1,4 +0,0 @@ -package heartbreak.thread - -// This function exists because WASI's pole_oneoff has quirky bugs when combined with pthreads -sleep :: (ms: i64) -> void #foreign "heartbreak" "thread_sleep" --- diff --git a/misc/onyx/heartbreak_timer.onyx b/misc/onyx/heartbreak_timer.onyx new file mode 100644 index 0000000..5b05fd3 --- /dev/null +++ b/misc/onyx/heartbreak_timer.onyx @@ -0,0 +1,15 @@ +package heartbreak.timer + +step :: () -> f32 #foreign "heartbreak" "timer_step" --- +getDelta :: () -> f32 #foreign "heartbreak" "timer_get_delta" --- +getFPS :: () -> f32 #foreign "heartbreak" "timer_get_fps" --- +getTime :: () -> f32 #foreign "heartbreak" "timer_get_time" --- + +sleep :: #match { + (milliseconds: i64) -> void #foreign "heartbreak" "timer_sleep" --- , + + macro (seconds: i64) -> void { + sleep :: sleep + sleep(milliseconds = seconds * 1000); + } +} \ No newline at end of file diff --git a/src/gfx.c b/src/gfx.c index 8917189..fd074b2 100644 --- a/src/gfx.c +++ b/src/gfx.c @@ -332,7 +332,7 @@ void gfx_immediate_renderer_push_triangle(ImmediateRenderer *ir, } void gfx_immediate_renderer_render_text(ImmediateRenderer *ir, f32 x, f32 y, char* msgptr, i32 msglen, f32 max_width) { - if (ir->tris_vertex_count > 0) gfx_immediate_renderer_flush(ir); + if (ir->tris_vertex_count > 0 || ir->world_transform_dirty) gfx_immediate_renderer_flush(ir); stbtt_aligned_quad* quads = (stbtt_aligned_quad *) alloca(msglen * sizeof(stbtt_aligned_quad)); i32 quad_num = 0; diff --git a/src/heartbreak.c b/src/heartbreak.c index fee8386..a125ba8 100644 --- a/src/heartbreak.c +++ b/src/heartbreak.c @@ -43,6 +43,7 @@ void build_heartbreak_imports(WasmFuncDefinition*** out_wfds, i32* out_count) { __heartbreak_module_graphics, __heartbreak_module_input, __heartbreak_module_thread, + __heartbreak_module_timer, __heartbreak_module_window, }; diff --git a/src/heartbreak_thread.c b/src/heartbreak_thread.c index 59c65e9..308a5d5 100644 --- a/src/heartbreak_thread.c +++ b/src/heartbreak_thread.c @@ -3,7 +3,6 @@ #ifdef _BH_LINUX #include #include - #include #endif #define HEARTBREAK_MODULE_NAME thread @@ -121,23 +120,9 @@ HEARTBREAK_DEF(kill, (WASM_I32), (WASM_I32)) { return NULL; } -HEARTBREAK_DEF(sleep, (WASM_I64), ()) { - i32 t = params->data[0].of.i64; - - #ifdef _BH_LINUX - usleep(t * 1000); - #endif - - #ifdef _BH_WINDOWS - Sleep(t); - #endif - return NULL; -} - HEARTBREAK_MODULE { HEARTBREAK_FUNC(spawn) HEARTBREAK_FUNC(kill) - HEARTBREAK_FUNC(sleep) NULL }; diff --git a/src/heartbreak_timer.c b/src/heartbreak_timer.c new file mode 100644 index 0000000..fc102a7 --- /dev/null +++ b/src/heartbreak_timer.c @@ -0,0 +1,74 @@ +#include "heartbreak.h" + +#ifdef _BH_LINUX + #include +#endif + +#define HEARTBREAK_MODULE_NAME timer + +static f64 last_queried_time = 0; +static f64 fps = 0; + +HEARTBREAK_DEF(step, (), (WASM_F32)) { + static i32 called_this_second = 0; + static f64 cumulative_delta = 0; + + f64 new_time = glfwGetTime(); + f64 delta = new_time - last_queried_time; + + called_this_second += 1; + cumulative_delta += delta; + + // If the integer parts are different, then we crossed the "second" boundary. + i32 i1 = (i32) new_time; + i32 i2 = (i32) last_queried_time; + if (i1 != i2) { + fps = called_this_second / cumulative_delta; + cumulative_delta = 0; + called_this_second = 0; + } + + last_queried_time = new_time; + + results->data[0] = WASM_F32_VAL(delta); + return NULL; +} + +HEARTBREAK_DEF(get_delta, (), (WASM_F32)) { + f64 new_time = glfwGetTime(); + results->data[0] = WASM_F32_VAL(new_time - last_queried_time); + return NULL; +} + +HEARTBREAK_DEF(get_fps, (), (WASM_F32)) { + results->data[0] = WASM_F32_VAL(fps); + return NULL; +} + +HEARTBREAK_DEF(get_time, (), (WASM_F32)) { + results->data[0] = WASM_F32_VAL(glfwGetTime()); + return NULL; +} + +HEARTBREAK_DEF(sleep, (WASM_I64), ()) { + i32 t = params->data[0].of.i64; + + #ifdef _BH_LINUX + usleep(t * 1000); + #endif + + #ifdef _BH_WINDOWS + Sleep(t); + #endif + return NULL; +} + +HEARTBREAK_MODULE { + HEARTBREAK_FUNC(step) + HEARTBREAK_FUNC(get_delta) + HEARTBREAK_FUNC(get_fps) + HEARTBREAK_FUNC(get_time) + HEARTBREAK_FUNC(sleep) + + NULL +}; \ No newline at end of file diff --git a/tests/simp.onyx b/tests/simp.onyx index 0c63e0d..26133d8 100644 --- a/tests/simp.onyx +++ b/tests/simp.onyx @@ -4,6 +4,7 @@ use package core dummy: hb.graphics.Image; squares: [..] Square; +square_resource: Resource(typeof squares) Square :: struct { x, y: f32; @@ -11,6 +12,27 @@ Square :: struct { r, g, b, a: f32; } +Resource :: struct (T: type_expr) { + resource: ^T; + lock: sync.Mutex; +} + +create_resource :: (v: ^$T) -> Resource(T) { + res: Resource(T); + res.resource = v; + sync.mutex_init(^res.lock); + return res; +} + +with_resource :: macro (r: ^Resource($T), block: Code) -> u32 { + sync.scoped_mutex(^r.lock); + + it := r.resource; + #insert block; + + return 0; +} + load :: () { w := hb.window.getWidth(); h := hb.window.getHeight(); @@ -23,10 +45,11 @@ load :: () { printf("{} by {}\n", w, h); array.init(^squares); + square_resource = create_resource(^squares); printf("Width is: {}\n", hb.graphics.getTextWidth("Hello, World!")); - better_font := hb.graphics.newFont("C:\\Windows\\Fonts\\calibri.ttf", 32); + better_font := hb.graphics.newFont("/usr/share/fonts/TTF/InputSerif-Regular.ttf", 32); assert(better_font != 0, "Failed to load font!"); hb.graphics.setFont(better_font); // hb.graphics.setFont(); @@ -37,7 +60,20 @@ load :: () { thread.spawn(^dummy_thread, cast(^void) null, (_) => { while true { println("Loop"); - hb.thread.sleep(1000); + with_resource(^square_resource, #code { + mx, my := hb.input.mouseGetPos(); + + *it << .{ + x = ~~mx, y = ~~my, + w = 20, h = 20, + r = random.float(0, 1), + g = random.float(0, 1), + b = random.float(0, 1), + a = 1, + }; + }); + + hb.timer.sleep(500); } }); } @@ -74,9 +110,9 @@ update :: (dt: f32) { hb.window.setShouldClose(true); } - if hb.input.mouseIsDown(.Left) { + if hb.input.mouseIsDown(.Left) do with_resource(^square_resource, #code { mx, my := hb.input.mouseGetPos(); - squares << .{ + *it << .{ x = ~~mx, y = ~~my, w = 10, h = 10, r = random.float(0, 1), @@ -84,7 +120,7 @@ update :: (dt: f32) { b = random.float(0, 1), a = random.float(0, 1), }; - } + }); } draw :: () { @@ -107,7 +143,7 @@ draw :: () { hb.graphics.setColor(1, 1, 1, 1); hb.graphics.drawImage(dummy, 100, 100, 100, 100); - hb.graphics.newQuad(4, 4, 8, 8, dummy) |> hb.graphics.drawQuad(200, 100); + hb.graphics.newQuad(4, 4, 8, 8, dummy) |> hb.graphics.drawQuad(200, 100, 100, 100); for it: squares { hb.graphics.setColor(it.r, it.g, it.b, it.a); diff --git a/tests/snake.onyx b/tests/snake.onyx new file mode 100644 index 0000000..9550c57 --- /dev/null +++ b/tests/snake.onyx @@ -0,0 +1,89 @@ +#load "./../misc/onyx/qhb" + +use package core + +Vec2 :: struct (T: type_expr) { + x, y: T; + + convert :: (use this: ^Vec2($T), $R: type_expr) -> Vec2(R) { + return .{ cast(R) x, cast(R) y }; + } +} +#operator + (v1, v2: Vec2($T)) => Vec2(T).{ v1.x + v2.x, v1.y + v2.y }; +#operator - (v1, v2: Vec2($T)) => Vec2(T).{ v1.x - v2.x, v1.y - v2.y }; +#operator * (v1: Vec2($T), s: T) => Vec2(T).{ v1.x * s, v1.y * s }; +#operator == (v1, v2: Vec2($T)) => v1.x == v2.x && v1.y == v2.y; + +Vec2i :: #type Vec2(i32); + +Snake :: struct { + head: Vec2i; + body: [..] Vec2i; + + direction: Vec2i; +} + +snake_make :: (head: Vec2i) -> Snake { + s: Snake; + s.head = head; + s.direction = .{ 1, 0 }; + array.init(^s.body); + for i: 4 do s.body << head; + return s; +} + +snake_move :: (use this: ^Snake) { + for i: iter.as_iterator(range.{ body.count - 1, 1, -1 }) { + body[i] = body[i - 1]; + } + body[0] = head; + + head += direction; +} + +snake_draw :: (use this: ^Snake, cell_size: f32) { + hb.graphics.setColor(0, 0.6, 0); + for ^it: body { + hb.graphics.rectangle(.Fill, ~~it.x * cell_size, ~~it.y * cell_size, cell_size, cell_size); + } + + hb.graphics.setColor(0, 1, 0); + hb.graphics.rectangle(.Fill, ~~head.x * cell_size, ~~head.y * cell_size, cell_size, cell_size); + + hb.graphics.setColor(0, 0, 0); + eye_pos := head->convert(f32) * cell_size + Vec2(f32).{ cell_size, cell_size } * 0.5; + eye_pos += direction->convert(f32) * 0.25 * cell_size; + hb.graphics.rectangle(.Fill, eye_pos.x - cell_size / 8, eye_pos.y - cell_size / 8, cell_size / 4, cell_size / 4); +} + + +the_snake: Snake; + +load :: () { + the_snake = snake_make(.{ 0, 0 }); +} + +update :: (dt: f32) { + if hb.input.keyIsDown(.Escape) { + hb.window.setShouldClose(true); + } + + if hb.input.keyIsDown(.Left) do the_snake.direction = .{ -1, 0 }; + if hb.input.keyIsDown(.Right) do the_snake.direction = .{ 1, 0 }; + if hb.input.keyIsDown(.Up) do the_snake.direction = .{ 0, -1 }; + if hb.input.keyIsDown(.Down) do the_snake.direction = .{ 0, 1 }; + + #persist snake_timer := 1.0f; + snake_timer -= dt; + if snake_timer <= 0 { + snake_timer = 0.2; + snake_move(^the_snake); + } +} + +draw :: () { + hb.graphics.setClearColor(0.1, 0.1, 0.1); + hb.graphics.clear(); + + snake_draw(^the_snake, 32); +} \ No newline at end of file -- 2.25.1