added asset loader to snake program
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Thu, 25 Nov 2021 04:44:52 +0000 (22:44 -0600)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Thu, 25 Nov 2021 04:44:52 +0000 (22:44 -0600)
misc/onyx/heartbreak_graphics.onyx
src/gfx.c
tests/snake.onyx

index f8c97bba304a38a5ddae87342bb5a6113cb10b30..77cac2addfa5bb2f26a6ccc49a0e898ab53d2c51 100644 (file)
@@ -3,7 +3,7 @@ package heartbreak.graphics
 // State
 
 setClearColor :: (r, g, b: f32, a: f32 = 1) -> void #foreign "heartbreak" "graphics_set_clear_color" ---
-setColor      :: (r, g, b: f32, a: f32 = 1) -> void        #foreign "heartbreak" "graphics_set_color" ---
+setColor      :: (r, g, b: f32, a: f32 = 1) -> void #foreign "heartbreak" "graphics_set_color" ---
 setLineWidth  :: (width: i32) -> void #foreign "heartbreak" "graphics_set_line_width" ---
 
 //
@@ -36,7 +36,8 @@ pop       :: ()           -> void #foreign "heartbreak" "graphics_pop" ---
 //
 // Images
 //
-Image :: #type i64
+Image :: #distinct i64
+#operator == macro (a, b: Image) => cast(i64) a == cast(i64) b;
 
 newImage  :: (path: str) -> Image #foreign "heartbreak" "graphics_image_load" ---
 drawImage :: (img: Image, x, y: f32, w: f32 = -1, h: f32 = -1) -> void #foreign "heartbreak" "graphics_image_draw" ---
@@ -101,7 +102,9 @@ drawQuad :: macro (quad: Quad, x, y: f32, w: f32 = -1, h: f32 = -1) {
 //
 // Fonts
 //
-Font :: #type i64
+Font :: #distinct i64
+#operator != macro (a, b: Font) => cast(i64) a != cast(i64) b;
+#operator == macro (a, b: Font) => cast(i64) a == cast(i64) b;
 
 newFont       :: (path: str, size: i32) -> Font #foreign "heartbreak" "graphics_font_load" ---
 setFont       :: (font: Font = 0) -> void #foreign "heartbreak" "graphics_font_set" ---
index fd074b2bce1754f08fcab62aa9d0930da0cb0fe6..f630fae8c42bc31829211e360a213ce2c44c8ed8 100644 (file)
--- a/src/gfx.c
+++ b/src/gfx.c
@@ -494,7 +494,14 @@ void gfx_immediate_renderer_shear(ImmediateRenderer *ir, f32 kx, f32 ky) {
 //
 Image* gfx_image_load(ImmediateRenderer *ir, const char* filename) {
     Image* img = malloc(sizeof(Image));
+
     img->img_data = stbi_load(filename, &img->width, &img->height, &img->channels, 4);
+    if (img->img_data == NULL) {
+        free(img);
+        logprint(ERROR, "Failed to load image '%s'.", filename);
+        return NULL;
+    }
+
     img->magic_number = 0xdeadbeefdeadbeef;
 
     glGenTextures(1, &img->texture);
@@ -528,9 +535,11 @@ void gfx_image_bind(ImmediateRenderer *ir, Image* img, u32 texture_unit) {
 }
 
 void gfx_image_unbind(ImmediateRenderer *ir) {
+    if (ir->active_image == NULL) return;
     if (ir->tris_vertex_count > 0) gfx_immediate_renderer_flush(ir);
 
     ir->active_image = NULL;
+    glUseProgram(ir->tris_shader.program);
     glUniform1f(ir->tris_shader.texture_enabled_uniform, 0);
 }
 
index 7a3d48c6db8741469822cf11d9bfacba2318259b..f16e05ca7e67e9e4418a772d8979be46d0411c04 100644 (file)
@@ -2,6 +2,55 @@
 
 use package core
 
+assets : struct {
+    ["./tests/player.png"]
+    player: hb.graphics.Image;
+
+    ["./assets/fonts/mononoki-Regular Nerd Font Complete Mono.ttf", 72]
+    title_font: hb.graphics.Font;
+}
+
+load_assets :: (assets: any) {
+    ptr_asset_info := cast(^type_info.Type_Info_Pointer) type_info.get_type_info(assets.type);
+    assert(ptr_asset_info.kind == .Pointer, "Loading assets only works for pointers to strucutres.");
+    asset_info := cast(^type_info.Type_Info_Struct) type_info.get_type_info(ptr_asset_info.to);
+    assert(asset_info.kind == .Struct, "Loading assets only works for pointers to strucutres.");
+
+    asset_store := cast(^u8) *cast(^rawptr) assets.data;
+
+    for ^asset: asset_info.members {
+        switch asset.type {
+            case hb.graphics.Image {
+                path := *(cast(^str) array.first(asset.tags, (t) => t.type == str).data);
+                img := hb.graphics.newImage(path);
+
+                if img == 0 {
+                    println("Failed to load image");
+                    continue;
+                }
+
+                @TODO // Add settings like scaling, filters, etc.
+
+                *cast(^hb.graphics.Image) (asset_store + asset.offset) = img;
+            }
+
+            case hb.graphics.Font {
+                path := *(cast(^str) array.first(asset.tags, (t) => t.type == str).data);
+                size := *(cast(^i32) array.first(asset.tags, (t) => t.type == i32).data);
+
+                font := hb.graphics.newFont(path, ~~size);
+
+                if font == 0 {
+                    println("Failed to load font");
+                    continue;
+                }
+
+                *cast(^hb.graphics.Font) (asset_store + asset.offset) = font;
+            }
+        }
+    }
+}
+
 Vec2 :: struct (T: type_expr) {
     x, y: T;
 
@@ -74,7 +123,7 @@ snake_draw :: (use this: ^Snake, cell_size: f32) {
 
 
 GameState :: enum { MainMenu; Playing; Paused; Lost; }
-state := GameState.Playing;
+state := GameState.MainMenu;
 
 the_snake: Snake;
 the_food: Vec2i;
@@ -82,6 +131,8 @@ the_food: Vec2i;
 load :: () {
     hb.window.setTitle("Snake 🐍");
 
+    load_assets(^assets);
+
     the_snake = snake_make(.{ 0, 0 });
     the_food = .{ 10, 10 };
 }
@@ -98,6 +149,13 @@ update :: (dt: f32) {
         hb.window.setShouldClose(true);
     }
 
+    if state == .MainMenu {
+        if hb.input.keyIsDown(.Space) {
+            state = .Playing;
+            return;
+        }
+    }
+
     if state != .Playing do return;
 
     #persist last_left_down := false;
@@ -142,17 +200,26 @@ draw :: () {
 
     ww, wh := hb.window.getDimensions();
 
+    centered_text :: macro (text: str, y: f32) {
+        width := hb.graphics.getTextWidth(text);
+        hb.graphics.print(text, (~~ww - width) / 2, y);
+    }
+
     switch state {
         case .MainMenu {
-            title := "Snake!";
-            title_width := hb.graphics.getTextWidth(title);
-            hb.graphics.print(title, (~~ww - title_width) / 2, 100);
+            hb.graphics.setFont(assets.title_font);
+            hb.graphics.setColor(0.2, 0.2, 0.2);
+            centered_text("Snake!", 100);
+
+            hb.graphics.setFont();
+            centered_text("Option 1", 200);
+            centered_text("Option 2", 240);
         }
 
         case .Playing {
             draw_game();
 
-            #if false {
+            #if true {
                 hb.graphics.setColor(1, 0, 0);
                 fps_buffer: [20] u8;
                 fps_str := conv.str_format(fps_buffer, "FPS: {}", cast(i32) hb.timer.getFPS());