added hb.timer; starting snake example
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Tue, 23 Nov 2021 18:25:52 +0000 (12:25 -0600)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Tue, 23 Nov 2021 18:25:52 +0000 (12:25 -0600)
12 files changed:
Makefile
build.bat
include/heartbreak.h
misc/onyx/heartbreak.onyx
misc/onyx/heartbreak_thread.onyx [deleted file]
misc/onyx/heartbreak_timer.onyx [new file with mode: 0644]
src/gfx.c
src/heartbreak.c
src/heartbreak_thread.c
src/heartbreak_timer.c [new file with mode: 0644]
tests/simp.onyx
tests/snake.onyx [new file with mode: 0644]

index 710797847a4cceb66126e68a13e322aa4f31e31f..80f79e4355a0841681d7670e64e1625aba3eac91 100644 (file)
--- 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
index da78133c02718ba63ea44522d7a7162155851c60..61d81511f02eb168c48c548e1c9cb27127aef972 100644 (file)
--- 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
index 5de3d3f7d7f37322a63cea42a5fcef89a8c8c0c9..05a70c8a785b72ab1eab2e8b28027026285699c9 100644 (file)
@@ -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
index eabaf958b998c176f827ef99861734e03249da90..0e0d62add41590bce2a8119e1b4054582d1f6bfb 100644 (file)
@@ -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 (file)
index c35e42d..0000000
+++ /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 (file)
index 0000000..5b05fd3
--- /dev/null
@@ -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
index 8917189256cada6a9cf96db1ce2ce943035cbec8..fd074b2bce1754f08fcab62aa9d0930da0cb0fe6 100644 (file)
--- 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;
index fee838609e3c0728d36fdbb5405b3f9ca1021d03..a125ba872cc692fca861eb64114b7fdd1faa5569 100644 (file)
@@ -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,
     };
 
index 59c65e9a45d7aedc032e407987ce3c755b76e4d5..308a5d5ca882fbfa63e57630d32e231d35516592 100644 (file)
@@ -3,7 +3,6 @@
 #ifdef _BH_LINUX
     #include <pthread.h>
     #include <signal.h>
-    #include <unistd.h>
 #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 (file)
index 0000000..fc102a7
--- /dev/null
@@ -0,0 +1,74 @@
+#include "heartbreak.h"
+
+#ifdef _BH_LINUX
+    #include <unistd.h>
+#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
index 0c63e0dd06540350375a599ead7bf8f510fd71bc..26133d8bda0ac6a6395ab55a2ae8b57a1e42cd4d 100644 (file)
@@ -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 (file)
index 0000000..9550c57
--- /dev/null
@@ -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