From 755518bb5d31e4ae3dfb8a51c2926e55978f7c80 Mon Sep 17 00:00:00 2001 From: geemili Date: Sat, 24 Feb 2024 15:58:55 -0700 Subject: [PATCH] feat: use framebuffer size to choose sprite set Using multiple sizes of sprites will hopefully make porting the game to a wider range of devices easier. --- src/main.zig | 192 +++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 171 insertions(+), 21 deletions(-) diff --git a/src/main.zig b/src/main.zig index b52440f..0be4bad 100644 --- a/src/main.zig +++ b/src/main.zig @@ -54,9 +54,10 @@ const TileSheet = struct { @floatFromInt(this.tile_size[0]), @floatFromInt(this.tile_size[1]), }; + // add a half to round up const size_in_tiles = [2]u32{ - @as(u32, @intCast(this.texture.size[0])) / this.tile_size[0], - @as(u32, @intCast(this.texture.size[1])) / this.tile_size[1], + (@as(u32, @intCast(this.texture.size[0])) + (this.tile_size[0] / 2)) / this.tile_size[0], + (@as(u32, @intCast(this.texture.size[1])) + (this.tile_size[1] / 2)) / this.tile_size[1], }; const pos_in_tiles = [2]u32{ tile_id % size_in_tiles[0], @@ -76,14 +77,30 @@ const TileSheet = struct { ((pos_in_tilesf[1] + 1) * tile_sizef[1]) / texture_sizef[1], }, }; - _ = canvas.printText(.{ 0, 0 }, "uv = {}", .{uv}, .{}); canvas.rect(pos, options.size orelse tile_sizef, .{ .texture = this.texture.glTexture, .uv = uv, }); } }; -// const cards_medium_spritesheet = + +/// A texture with a regular grid of sprites +const DeckSprites = struct { + tilesheet: TileSheet, + /// Return the tile index for a given card, + mapping: std.AutoHashMapUnmanaged(Card, u32), + blank: u32, + back: u32, + + pub fn deinit(this: *@This(), gpa: std.mem.Allocator) void { + this.tilesheet.deinit(); + this.mapping.deinit(gpa); + } + + pub fn getTileForCard(this: @This(), card: Card) u32 { + return this.mapping.get(card) orelse 0; + } +}; pub fn main() !void { var gpa = std.heap.GeneralPurposeAllocator(.{}){}; @@ -118,16 +135,11 @@ pub fn main() !void { // Set up input callbacks _ = seizer.backend.glfw.c.glfwSetFramebufferSizeCallback(window, &glfw_framebuffer_size_callback); - var card_tilemap = try TileSheet.init(.{ - .allocator = gpa.allocator(), - .image_file_contents = @embedFile("./cardsMedium_tilemap.png"), - .tile_size = .{ 32, 32 }, - .texture_options = .{ - .min_filter = .nearest, - .mag_filter = .nearest, - }, - }); - defer card_tilemap.deinit(); + var card_tilemap_medium: ?DeckSprites = null; + defer if (card_tilemap_medium) |*tilemap| tilemap.deinit(gpa.allocator()); + + var card_tilemap_large: ?DeckSprites = null; + defer if (card_tilemap_large) |*tilemap| tilemap.deinit(gpa.allocator()); var canvas = try seizer.Canvas.init(gpa.allocator(), .{}); defer canvas.deinit(gpa.allocator()); @@ -154,19 +166,157 @@ pub fn main() !void { @floatFromInt(framebuffer_size[1]), }, }); - card_tilemap.renderTile(&canvas, 1, .{ 10, 10 }, .{}); - // canvas.rect(.{ 10, 10 }, .{ @as(f32, @floatFromInt(card_tilemap.size[0])) / 15.0, @as(f32, @floatFromInt(card_tilemap.size[1])) / 10.0 }, .{ - // .texture = card_tilemap.glTexture, - // .uv = .{ .min = .{ 0, 0 }, .max = .{ 1.0 / 15.0, 1.0 / 10.0 } }, - // }); - _ = canvas.writeText(.{ 50, 50 }, "Hello, world!", .{}); - _ = canvas.printText(.{ 50, 100 }, "window_size = {}, {}\nframebuffer_size = {}, {}", .{ window_size[0], window_size[1], framebuffer_size[0], framebuffer_size[1] }, .{}); + + const render_large = framebuffer_size[1] > 1000; + if (render_large) { + if (card_tilemap_large == null) { + card_tilemap_large = try loadLargeCards(gpa.allocator()); + } + } else { + if (card_tilemap_medium == null) { + card_tilemap_medium = try loadMediumCards(gpa.allocator()); + } + } + + const deck_sprites = if (framebuffer_size[1] > 1000) card_tilemap_large.? else card_tilemap_medium.?; + for (0..4) |suit| { + for (1..14) |rank| { + const card = Card{ .suit = @enumFromInt(suit), .rank = @intCast(rank) }; + const tile_index = deck_sprites.getTileForCard(card); + deck_sprites.tilesheet.renderTile(&canvas, @intCast(tile_index), .{ + @floatFromInt(rank * deck_sprites.tilesheet.tile_size[0]), + @floatFromInt(suit * deck_sprites.tilesheet.tile_size[1]), + }, .{}); + } + } + + deck_sprites.tilesheet.renderTile(&canvas, deck_sprites.blank, .{ + @floatFromInt(1 * deck_sprites.tilesheet.tile_size[0]), + @floatFromInt(5 * deck_sprites.tilesheet.tile_size[1]), + }, .{}); + deck_sprites.tilesheet.renderTile(&canvas, deck_sprites.back, .{ + @floatFromInt(2 * deck_sprites.tilesheet.tile_size[0]), + @floatFromInt(5 * deck_sprites.tilesheet.tile_size[1]), + }, .{}); + canvas.end(); seizer.backend.glfw.c.glfwSwapBuffers(window); } } +fn loadMediumCards(gpa: std.mem.Allocator) !DeckSprites { + var tilesheet = try TileSheet.init(.{ + .allocator = gpa, + .image_file_contents = @embedFile("./cardsMedium_tilemap.png"), + .tile_size = .{ 33, 33 }, + .texture_options = .{ + .min_filter = .nearest, + .mag_filter = .nearest, + }, + }); + errdefer tilesheet.deinit(); + + var mapping = std.AutoHashMap(Card, u32).init(gpa); + errdefer mapping.deinit(); + try mapping.ensureTotalCapacity(52); + + const hearts_start_index: u32 = 0; + for (0..13) |rank| { + mapping.putAssumeCapacityNoClobber( + Card{ .suit = .hearts, .rank = @intCast(rank + 1) }, + hearts_start_index + @as(u32, @intCast(rank)), + ); + } + + const diamonds_start_index: u32 = 15; + for (0..13) |rank| { + mapping.putAssumeCapacityNoClobber( + Card{ .suit = .diamonds, .rank = @intCast(rank + 1) }, + diamonds_start_index + @as(u32, @intCast(rank)), + ); + } + + const clubs_start_index: u32 = 30; + for (0..13) |rank| { + mapping.putAssumeCapacityNoClobber( + Card{ .suit = .clubs, .rank = @intCast(rank + 1) }, + clubs_start_index + @as(u32, @intCast(rank)), + ); + } + + const spades_start_index: u32 = 45; + for (0..13) |rank| { + mapping.putAssumeCapacityNoClobber( + Card{ .suit = .spades, .rank = @intCast(rank + 1) }, + spades_start_index + @as(u32, @intCast(rank)), + ); + } + + return DeckSprites{ + .tilesheet = tilesheet, + .mapping = mapping.unmanaged, + .blank = 14, + .back = 29, + }; +} + +fn loadLargeCards(gpa: std.mem.Allocator) !DeckSprites { + var tilesheet = try TileSheet.init(.{ + .allocator = gpa, + .image_file_contents = @embedFile("./cardsLarge_tilemap.png"), + .tile_size = .{ 65, 65 }, + .texture_options = .{ + .min_filter = .nearest, + .mag_filter = .nearest, + }, + }); + errdefer tilesheet.deinit(); + + var mapping = std.AutoHashMap(Card, u32).init(gpa); + errdefer mapping.deinit(); + try mapping.ensureTotalCapacity(52); + + const hearts_start_index: u32 = 0; + for (0..13) |rank| { + mapping.putAssumeCapacityNoClobber( + Card{ .suit = .hearts, .rank = @intCast(rank + 1) }, + hearts_start_index + @as(u32, @intCast(rank)), + ); + } + + const diamonds_start_index: u32 = 14; + for (0..13) |rank| { + mapping.putAssumeCapacityNoClobber( + Card{ .suit = .diamonds, .rank = @intCast(rank + 1) }, + diamonds_start_index + @as(u32, @intCast(rank)), + ); + } + + const clubs_start_index: u32 = 28; + for (0..13) |rank| { + mapping.putAssumeCapacityNoClobber( + Card{ .suit = .clubs, .rank = @intCast(rank + 1) }, + clubs_start_index + @as(u32, @intCast(rank)), + ); + } + + const spades_start_index: u32 = 42; + for (0..13) |rank| { + mapping.putAssumeCapacityNoClobber( + Card{ .suit = .spades, .rank = @intCast(rank + 1) }, + spades_start_index + @as(u32, @intCast(rank)), + ); + } + + return DeckSprites{ + .tilesheet = tilesheet, + .mapping = mapping.unmanaged, + .blank = 13, + .back = 27, + }; +} + fn glfw_framebuffer_size_callback(window: ?*seizer.backend.glfw.c.GLFWwindow, width: c_int, height: c_int) callconv(.C) void { _ = window; gl.viewport(