diff --git a/assets/tiled/map.tmx b/assets/tiled/map.tmx index f392817..c10b77c 100644 --- a/assets/tiled/map.tmx +++ b/assets/tiled/map.tmx @@ -1,5 +1,5 @@ - + @@ -73,4 +73,9 @@ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + + + + + diff --git a/assets/tiled/sokoban.tiled-session b/assets/tiled/sokoban.tiled-session index 7fda9ae..cf7d775 100644 --- a/assets/tiled/sokoban.tiled-session +++ b/assets/tiled/sokoban.tiled-session @@ -1,17 +1,31 @@ { - "activeFile": "", + "activeFile": "map.tmx", "expandedProjectPaths": [ ], + "file.lastUsedOpenFilter": "All Files (*)", "fileStates": { + "map.tmx": { + "scale": 1, + "selectedLayer": 3, + "viewCenter": { + "x": 624.5, + "y": 421 + } + }, + "tileset.tsx": { + "scaleInDock": 1 + } }, "last.imagePath": "/home/rokas/code/games/sokol-template-v2/assets/sokoban", "map.lastUsedFormat": "tmx", "map.tileHeight": 64, "map.tileWidth": 64, "openFiles": [ + "map.tmx" ], "project": "sokoban.tiled-project", "recentFiles": [ + "map.tmx" ], "tileset.lastUsedFormat": "tsx", "tileset.tileSize": { diff --git a/libs/tiled/src/layer.zig b/libs/tiled/src/layer.zig index 4efc102..eb05984 100644 --- a/libs/tiled/src/layer.zig +++ b/libs/tiled/src/layer.zig @@ -98,7 +98,16 @@ pub const ObjectVariant = struct { color: ?Color, draw_order: DrawOrder, - items: []Object + items: []Object, + + pub fn getByName(self: *const ObjectVariant, name: []const u8) ?*const Object { + for (self.items) |*item| { + if (std.mem.eql(u8, item.name, name)) { + return item; + } + } + return null; + } }; pub const GroupVariant = struct { diff --git a/libs/tiled/src/tilemap.zig b/libs/tiled/src/tilemap.zig index c6cde2e..edcbfa4 100644 --- a/libs/tiled/src/tilemap.zig +++ b/libs/tiled/src/tilemap.zig @@ -77,6 +77,14 @@ pub const Tile = struct { pub fn getProperties(self: Tile) Property.List { return self.tileset.getTileProperties(self.id) orelse .empty; } + + pub fn getPositionInImage(self: Tile) Position { + return self.tileset.getTilePositionInImage(self.id); + } + + pub fn getPosition(self: Tile) Tileset.TilePosition { + return self.tileset.getTilePosition(self.id); + } }; arena: std.heap.ArenaAllocator, @@ -230,6 +238,15 @@ pub fn getTile(self: *const Tilemap, layer: *const Layer, tilesets: Tileset.List }; } +pub fn getLayer(self: *const Tilemap, name: []const u8) ?*const Layer { + for (self.layers) |*layer| { + if (std.mem.eql(u8, layer.name, name)) { + return layer; + } + } + return null; +} + pub fn deinit(self: *const Tilemap) void { self.arena.deinit(); } diff --git a/libs/tiled/src/tileset.zig b/libs/tiled/src/tileset.zig index 04b9305..f5e5801 100644 --- a/libs/tiled/src/tileset.zig +++ b/libs/tiled/src/tileset.zig @@ -1,5 +1,6 @@ const std = @import("std"); const Io = std.Io; +const assert = std.debug.assert; const Allocator = std.mem.Allocator; const xml = @import("./xml.zig"); @@ -204,21 +205,32 @@ pub fn getTileProperties(self: *const Tileset, id: u32) ?Property.List { return null; } -pub fn getTilePositionInImage(self: *const Tileset, id: u32) ?Position { - if (id >= self.tile_count) { - return null; - } +pub const TilePosition = struct { + x: usize, + y: usize, +}; + +pub fn getTilePosition(self: *const Tileset, id: u32) TilePosition { + assert(id < self.tile_count); const tileset_width = @divExact(self.image.width, self.tile_width); const tile_x = @mod(id, tileset_width); const tile_y = @divFloor(id, tileset_width); - return Position{ - .x = @floatFromInt(tile_x * self.tile_width), - .y = @floatFromInt(tile_y * self.tile_height), + return .{ + .x = tile_x, + .y = tile_y, }; +} +pub fn getTilePositionInImage(self: *const Tileset, id: u32) Position { + const pos = self.getTilePosition(id); + + return Position{ + .x = @floatFromInt(pos.x * self.tile_width), + .y = @floatFromInt(pos.y * self.tile_height), + }; } pub fn deinit(self: *const Tileset) void { diff --git a/src/app.zig b/src/app.zig index 8f8809d..02b9a87 100644 --- a/src/app.zig +++ b/src/app.zig @@ -1,5 +1,6 @@ const std = @import("std"); const log = std.log.scoped(.app); +const assert = std.debug.assert; const Allocator = std.mem.Allocator; const Math = @import("math"); @@ -40,11 +41,46 @@ const PlayerSprites = struct { } }; -show_first_window: bool, -check: bool, +const Tilemap = struct { + tiles: []Tile, + width: usize, + height: usize, + + const Tile = struct { + ground_sprite: ?Gfx.Sprite.Id, + wall_sprite: ?Gfx.Sprite.Id, + + const empty = Tile{ + .ground_sprite = null, + .wall_sprite = null + }; + }; + + pub fn init(gpa: Allocator, width: usize, height: usize) !Tilemap { + const tiles = try gpa.alloc(Tile, width * height); + @memset(tiles, .empty); + + return Tilemap{ + .width = width, + .height = height, + .tiles = tiles + }; + } + + pub fn deinit(self: *Tilemap, gpa: Allocator) void { + gpa.free(self.tiles); + } + + pub fn get(self: *Tilemap, x: usize, y: usize) *Tile { + assert(x < self.width); + assert(y < self.height); + return &self.tiles[y * self.width + x]; + } +}; + player_pos: Vec2, -wall_sprite: Gfx.Sprite.Id, roboto_font: Gfx.Font.Id, +tilemap: Tilemap, tilesheet: Tilesheet, @@ -65,17 +101,19 @@ const Tilesheet = struct { width: u32, height: u32, - pub fn init(gpa: Allocator, image: Gfx.ImageData, width: u32, height: u32, tile_size: Vec2) !Tilesheet { + pub fn init(gpa: Allocator, image: Gfx.ImageData, tile_width: u32, tile_height: u32) !Tilesheet { + const width = image.width / tile_width; + const height = image.height / tile_height; const sprites = try gpa.alloc(Gfx.Sprite.Id, width * height); errdefer gpa.free(sprites); @memset(sprites, .nil); return Tilesheet{ .sprites = sprites, - .image = image, + .image = image, .width = width, .height = height, - .tile_size = tile_size + .tile_size = .initFromInt(u32, tile_width, tile_height) }; } @@ -85,7 +123,7 @@ const Tilesheet = struct { } } - pub fn get(self: Tilesheet, x: u32, y: u32) Gfx.Sprite.Id { + pub fn get(self: Tilesheet, x: usize, y: usize) Gfx.Sprite.Id { if (x >= self.width or y >= self.height) { log.warn("Attempt to get tile which is out of bounds", .{}); return .nil; @@ -109,24 +147,6 @@ pub fn init(self: *App, plt: Platform.Init) !?u8 { const robot_font = Gfx.initFont(); Gfx.setFont(robot_font, plt.assets.readFile("roboto-font/Roboto-Regular.ttf")); - var scratch = std.heap.ArenaAllocator.init(plt.gpa); - defer scratch.deinit(); - - var buffers = Tiled.xml.Lexer.Buffers.init(plt.gpa); - defer buffers.deinit(); - - const tilemap = try Tiled.Tilemap.initFromBuffer(plt.gpa, &scratch, &buffers, plt.assets.readFile("tiled/map.tmx")); - defer tilemap.deinit(); - - var tilesets: Tiled.Tileset.List = .empty; - defer tilesets.deinit(plt.gpa); - - try tilesets.add(plt.gpa, "tiled/tilemap.tsx", try Tiled.Tileset.initFromBuffer(plt.gpa, &scratch, &buffers, plt.assets.readFile("tiled/tileset.tsx"))); - - for (tilemap.layers) |layer| { - std.debug.print("{s}\n", .{layer.name}); - } - const tilesheet_png = try STBImage.load(plt.assets.readFile("sokoban/sokoban_tilesheet.png")); const tilesheet = try Tilesheet.init( plt.arena, @@ -135,13 +155,9 @@ pub fn init(self: *App, plt: Platform.Init) !?u8 { .height = tilesheet_png.height, .pixels = .{ .rgba8 = tilesheet_png.rgba8_pixels } }, - 13, - 8, - .init(64, 64) + 64, 64, ); - const wall_sprite = tilesheet.get(1, 0); - var player_sprites: PlayerSprites = undefined; inline for (.{ .{ @@ -172,43 +188,114 @@ pub fn init(self: *App, plt: Platform.Init) !?u8 { }); self.* = App{ - .player_pos = .init(100, 100), - .show_first_window = true, - .check = false, + .player_pos = .init(0, 0), .player_sprites = player_sprites, - .wall_sprite = wall_sprite, .roboto_font = robot_font, .player_direction = .left, .player_frame_index = 0, .animation_timer = 0, .tilesheet = tilesheet, .footstep_buffer = footstep_sound, - .sfx_bus = Audio.addBus(.{ .label = "sfx" }) + .sfx_bus = Audio.addBus(.{ .label = "sfx" }), + .tilemap = try Tilemap.init(plt.gpa, 30, 20) }; + { + var scratch = std.heap.ArenaAllocator.init(plt.gpa); + defer scratch.deinit(); + + var buffers = Tiled.xml.Lexer.Buffers.init(plt.gpa); + defer buffers.deinit(); + + const tilemap = try Tiled.Tilemap.initFromBuffer(plt.gpa, &scratch, &buffers, plt.assets.readFile("tiled/map.tmx")); + defer tilemap.deinit(); + + var tilesets: Tiled.Tileset.List = .empty; + defer tilesets.deinit(plt.gpa); + + try tilesets.add(plt.gpa, "tileset.tsx", try Tiled.Tileset.initFromBuffer(plt.gpa, &scratch, &buffers, plt.assets.readFile("tiled/tileset.tsx"))); + + const ground = tilemap.getLayer("Ground").?; + const walls = tilemap.getLayer("Walls").?; + for (0..tilemap.height) |y| { + for (0..tilemap.width) |x| { + const tile = self.tilemap.get(x, y); + + if (tilemap.getTile(ground, tilesets, x, y)) |tiled_tile| { + const pos = tiled_tile.getPosition(); + tile.ground_sprite = tilesheet.get(pos.x, pos.y); + } + + if (tilemap.getTile(walls, tilesets, x, y)) |tiled_tile| { + const pos = tiled_tile.getPosition(); + tile.wall_sprite = tilesheet.get(pos.x, pos.y); + } + } + } + + const markers = &tilemap.getLayer("Markers").?.variant.object; + const spawn = markers.getByName("spawn").?; + self.player_pos.x = @divFloor(spawn.shape.point.x, @as(f32, @floatFromInt(tilemap.tile_width))); + self.player_pos.y = @divFloor(spawn.shape.point.y, @as(f32, @floatFromInt(tilemap.tile_height))); + } + + { + var min_x: usize = self.tilemap.width; + var max_x: usize = 0; + var min_y: usize = self.tilemap.height; + var max_y: usize = 0; + for (0..self.tilemap.height) |y| { + for (0..self.tilemap.width) |x| { + const tile = self.tilemap.get(x, y); + if (tile.ground_sprite != null or tile.wall_sprite != null) { + min_x = @min(min_x, x); + min_y = @min(min_y, y); + max_x = @max(max_x, x); + max_y = @max(max_y, y); + } + } + } + + const map_width = max_x - min_x + 1; + const map_height = max_y - min_y + 1; + + var new_tilemap = try Tilemap.init(plt.gpa, map_width, map_height); + for (0..map_height) |y| { + for (0..map_width) |x| { + new_tilemap.get(x, y).* = self.tilemap.get(min_x + x, min_y + y).*; + } + } + + self.tilemap.deinit(plt.gpa); + self.tilemap = new_tilemap; + + self.player_pos.x -= @floatFromInt(min_x); + self.player_pos.y -= @floatFromInt(min_y); + } + return null; } pub fn frame(self: *App, plt: Platform.Frame) !void { const input = plt.input; - const dt = plt.deltaTime(); - var dir: Vec2 = .init(0, 0); - if (input.isKeyDown(.S)) { + if (input.isKeyPressed(.S)) { dir.y += 1; } - if (input.isKeyDown(.W)) { + if (input.isKeyPressed(.W)) { dir.y -= 1; } - if (input.isKeyDown(.D)) { + if (input.isKeyPressed(.D)) { dir.x += 1; } - if (input.isKeyDown(.A)) { + if (input.isKeyPressed(.A)) { dir.x -= 1; } - dir = dir.normalized(); - self.player_pos = self.player_pos.add(dir.multiplyScalar(50 * dt)); + if (dir.x != 0) { + dir.y = 0; + } + self.player_pos = self.player_pos.add(dir); if (input.isKeyPressed(.F)) { Audio.stop(self.footstep_sound); @@ -250,16 +337,30 @@ pub fn frame(self: *App, plt: Platform.Frame) !void { } Gfx.setClearColor(.rgb(40, 40, 40)); - Gfx.drawSprite(frames[self.player_frame_index], self.player_pos, .init(64, 64), .white); - Gfx.drawSprite(self.tilesheet.get(11, 6), self.player_pos.add(.init(0, 200)), .init(64, 64), .white); + + const tile_size = Vec2.init(48, 48); + for (0..self.tilemap.height) |y| { + for (0..self.tilemap.width) |x| { + const tile = self.tilemap.get(x, y); + + if (tile.ground_sprite) |sprite| { + Gfx.drawSprite(sprite, Vec2.initFromInt(usize, x, y).multiply(tile_size), tile_size, .white); + } + + if (tile.wall_sprite) |sprite| { + Gfx.drawSprite(sprite, Vec2.initFromInt(usize, x, y).multiply(tile_size), tile_size, .white); + } + } + } + + Gfx.drawSprite(frames[self.player_frame_index], self.player_pos.multiply(tile_size), tile_size, .white); Gfx.drawText(self.roboto_font, .{ .pos = .init(300, 100), - .text = "Hello, World!", + .text = try std.fmt.allocPrint(plt.frame, "Hello, World! {:.0}", .{ plt.time() }), .height = 64, }); } pub fn deinit(self: *App, plt: Platform.Deinit) void { - _ = plt; // autofix - _ = self; // autofix + self.tilemap.deinit(plt.gpa); } diff --git a/src/platform/audio.zig b/src/platform/audio.zig index 448c86b..07e976f 100644 --- a/src/platform/audio.zig +++ b/src/platform/audio.zig @@ -171,12 +171,6 @@ pub fn init(io: Io, gpa: std.mem.Allocator, opts: InitOptions) !void { @memset(self.thread_state.elapsed_time_ms.?, 0); } - log.debug("Init:", .{}); - log.debug("- sample_rate: {}", .{saudio.sampleRate()}); - log.debug("- channels: {}", .{saudio.channels()}); - log.debug("- buffer_frames: {}", .{saudio.bufferFrames()}); - log.debug("- max callback duration: {f}", .{getMaxAudioThreadDuration()}); - self.running.store(true, .seq_cst); } @@ -376,7 +370,7 @@ fn busMultipliedVolume(id: BusId) f32 { const PlayOptions = struct { buffer: BufferId, - // TODO: adsr: ?ADSR = null, + // TODO: adsr: ?Sound.ADSR = null, volume: f32 = 1, loop: bool = false, bus: ?BusId = null @@ -458,6 +452,11 @@ pub fn showDebug() void { if (ImGUI.beginTabItem("General")) { defer ImGUI.endTabItem(); + ImGUI.text("Sample rate: {}Hz", .{saudio.sampleRate()}); + ImGUI.text("Buffer frames: {}", .{saudio.bufferFrames()}); + ImGUI.text("Max callback duration: {f}", .{getMaxAudioThreadDuration()}); + + ImGUI.separator(); ImGUI.text("Buffers: {}/{}", .{buffers.items.len, buffers.capacity}); ImGUI.text("Buses: {}/{}", .{buses.items.len, buses.capacity}); ImGUI.text("Sounds: {}/{}", .{sounds.count(), sounds.slots.capacity}); diff --git a/src/platform/graphics.zig b/src/platform/graphics.zig index 1ffd88d..cd5cb1d 100644 --- a/src/platform/graphics.zig +++ b/src/platform/graphics.zig @@ -233,9 +233,6 @@ pub fn init(gpa: std.mem.Allocator, logger: sg.Logger) !void { } }); } - - repackSpritesheetIfNeeded(self.default_spritesheet); - rebuildSpritesheetTextureIfNeeded(self.default_spritesheet); } pub fn deinit() void { @@ -347,45 +344,35 @@ pub fn beginFrame() void { .swapchain = sglue.swapchain() }); - const default_spritesheet = self.spritesheets.getAssumeExists(self.default_spritesheet); - const spritesheet_texture = self.textures.getAssumeExists(default_spritesheet.texture); - self.bindings.views[shd.VIEW_tex] = spritesheet_texture.view; + self.bindings.views[shd.VIEW_tex].id = sg.invalid_id; self.bindings.samplers[shd.SMP_smp] = self.linear_sampler; } pub fn showDebug() void { const self = &g_state; - _ = self; // autofix // TODO: Improve this by using tables or somekind of filtering - // if (ImGUI.beginWindow(.{ - // .name = "graphics", - // .size = .init(200, 200) - // })) { - // defer ImGUI.endWindow(); - // - // { - // ImGUI.text("Spritesheets:", .{}); - // var spritesheet_iter = self.spritesheets.iterator(); - // while (spritesheet_iter.next()) |spritesheet_id| { - // const spritesheet = self.spritesheets.getAssumeExists(spritesheet_id); - // const texture = self.textures.get(spritesheet.texture); - // ImGUI.text("{f}:", .{spritesheet_id}); - // ImGUI.text(" - needs_texture_rebuild:{}", .{spritesheet.needs_texture_rebuild}); - // ImGUI.text(" - texture:{?}", .{texture}); - // } - // } - // - // { - // ImGUI.text("Sprites:", .{}); - // var sprite_iter = self.sprites.iterator(); - // while (sprite_iter.next()) |sprite_id| { - // const sprite = self.sprites.getAssumeExists(sprite_id); - // ImGUI.text("{f} - {?}", .{sprite_id, sprite.position}); - // } - // } - // } + { + ImGUI.text("Spritesheets:", .{}); + var spritesheet_iter = self.spritesheets.iterator(); + while (spritesheet_iter.next()) |spritesheet_id| { + const spritesheet = self.spritesheets.getAssumeExists(spritesheet_id); + ImGUI.text("{f}:", .{spritesheet_id}); + ImGUI.text(" - needs_texture_rebuild:{}", .{spritesheet.needs_texture_rebuild}); + ImGUI.text(" - texture:{f}", .{spritesheet.texture}); + ImGUI.text(" - size:{}", .{spritesheet.size}); + } + } + + { + ImGUI.text("Sprites:", .{}); + var sprite_iter = self.sprites.iterator(); + while (sprite_iter.next()) |sprite_id| { + const sprite = self.sprites.getAssumeExists(sprite_id); + ImGUI.text("{f} - {?}", .{sprite_id, sprite.position}); + } + } } // TODO: This will always have a 1-frame delay. @@ -396,11 +383,7 @@ pub fn setClearColor(color: Color) void { self.clear_color = color; } -const FlushOptions = struct { - rebuild_spritesheets: bool = true -}; - -pub fn flush(opts: FlushOptions) void { +pub fn flush() void { const self = &g_state; if (self.quads.items.len == 0) { @@ -408,17 +391,20 @@ pub fn flush(opts: FlushOptions) void { } defer self.quads.clearRetainingCapacity(); - if (opts.rebuild_spritesheets) { - var spritesheet_iter = self.spritesheets.iterator(); - while (spritesheet_iter.next()) |spritesheet_id| { - const spritesheet = self.spritesheets.getAssumeExists(spritesheet_id); - const texture = self.textures.get(spritesheet.texture) orelse continue; - if (isViewBound(texture.view)) { - rebuildSpritesheetTextureIfNeeded(spritesheet_id); - } + var spritesheet_iter = self.spritesheets.iterator(); + while (spritesheet_iter.next()) |spritesheet_id| { + const spritesheet = self.spritesheets.getAssumeExists(spritesheet_id); + const texture = self.textures.get(spritesheet.texture) orelse continue; + if (isViewBound(texture.view)) { + rebuildSpritesheetTextureIfNeeded(spritesheet_id); } } + if (sg.queryViewState(getBoundView().*) != .VALID) { + log.warn("Attempt to use view which isn't valid", .{}); + return; + } + const vertex_buffer = self.bindings.vertex_buffers[0]; const data = sg.asRange(self.quads.items); @@ -442,7 +428,7 @@ pub fn flush(opts: FlushOptions) void { var fs_params: shd.FsParams = .{ .texture_mode = 0, }; - const image = sg.queryViewImage(self.bindings.views[shd.VIEW_tex]); + const image = sg.queryViewImage(getBoundView().*); if (sg.queryImagePixelformat(image) == .R8) { fs_params.texture_mode = 1; } @@ -451,17 +437,17 @@ pub fn flush(opts: FlushOptions) void { sg.applyBindings(self.bindings); sg.applyUniforms(shd.UB_vs_params, sg.asRange(&vs_params)); sg.applyUniforms(shd.UB_fs_params, sg.asRange(&fs_params)); + assert(self.quads.items.len > 0); sg.draw(0, @intCast(self.quads.items.len * 6), 1); } pub fn endFrame() void { const self = &g_state; - flush(.{}); + flush(); sg.endPass(); sg.commit(); - self.frame_index += 1; var spritesheet_iter = self.spritesheets.iterator(); while (spritesheet_iter.next()) |spritesheet_id| { @@ -473,6 +459,8 @@ pub fn endFrame() void { } else if (self.transforms.items.len == 0) { log.warn("Too many calls to transformPop()", .{}); } + + self.frame_index += 1; } pub fn drawRectangle(pos: Vec2, size: Vec2, color: Color) void { @@ -577,33 +565,28 @@ pub fn initTexture(opts: TextureOptions) Texture.Id { return id; } -fn isViewBound(view: sg.View) bool { +fn getBoundView() *sg.View { const self = &g_state; - const bound_view = &self.bindings.views[shd.VIEW_tex]; - return bound_view.id == view.id; + return &self.bindings.views[shd.VIEW_tex]; } -fn deinitTextureResources(texture: *Texture) void { - const self = &g_state; - - if (isViewBound(texture.view)) { - const default_sprite = self.sprites.getAssumeExists(self.default_sprite); - const default_spritesheet = self.spritesheets.getAssumeExists(default_sprite.spritesheet); - const default_texture = self.textures.getAssumeExists(default_spritesheet.texture); - bindView(default_texture.view, .{ .rebuild_spritesheets = false }); - } - - sg.destroyImage(texture.image); +fn isViewBound(view: sg.View) bool { + return getBoundView().id == view.id; } pub fn deinitTexture(id: Texture.Id) void { const self = &g_state; - if (id == self.nil_texture) { + if (id == .nil) { return; } if (self.textures.get(id)) |texture| { - deinitTextureResources(texture); + const bound_view = getBoundView(); + if (bound_view.id == texture.view.id) { + bound_view.id = sg.invalid_id; + } + + sg.destroyImage(texture.image); sg.destroyView(texture.view); self.textures.removeAssumeExists(id); } @@ -649,7 +632,6 @@ pub fn setTexture(id: Texture.Id, texture_data: ImageData) void { var texture_updated: bool = false; if (sg.queryImageState(texture.image) == .VALID) { - // sg.queryImageInfo const image_desc = sg.queryImageDesc(texture.image); if (image_desc.width == new_image_desc.width and image_desc.height == new_image_desc.height and @@ -659,19 +641,19 @@ pub fn setTexture(id: Texture.Id, texture_data: ImageData) void { image_desc.usage.immutable == new_image_desc.usage.immutable and image_desc.usage.dynamic_update ) { - log.debug("Update texture '{f}'", .{id}); + log.debug("[{}] Update texture '{f}'", .{self.frame_index, id}); if (texture.updated_at == null or texture.updated_at.? != self.frame_index) { sg.updateImage(texture.image, image_data); texture_updated = true; texture.updated_at = self.frame_index; } else { - log.warn("Attempt to update the same texture multiple times per frame", .{}); + log.warn("Attempt to update the same texture multiple times per frame, texture: {f}", .{id}); } } } if (!texture_updated) { - log.debug("Create image-view, texture={f}", .{id}); + log.debug("[{}] Create image-view, texture={f}", .{self.frame_index, id}); const new_image = sg.makeImage(new_image_desc); if (sg.queryImageState(new_image) != .VALID) { log.warn("makeImage() failed", .{}); @@ -689,11 +671,13 @@ pub fn setTexture(id: Texture.Id, texture_data: ImageData) void { return; } - deinitTextureResources(texture); + sg.destroyImage(texture.image); texture.image = new_image; texture.updated_at = null; + log.debug("is dynamic {f}, {}", .{id, texture.dynamic}); if (texture.dynamic) { + log.debug("update texture {f}", .{id}); sg.updateImage(new_image, image_data); texture.updated_at = self.frame_index; } @@ -712,20 +696,11 @@ pub fn drawTexture(id: Texture.Id, pos: Vec2, size: Vec2, color: Color) void { }); } -fn bindView(view: sg.View, opts: FlushOptions) void { +fn bindView(view: sg.View) void { + _ = view; // autofix const self = &g_state; + _ = self; // autofix - const view_state = sg.queryViewState(view); - if (view_state != .VALID and view_state != .ALLOC) { - log.warn("Attempt to bind non-valid view: state={}", .{view_state}); - return; - } - - const bound_view = &self.bindings.views[shd.VIEW_tex]; - if (bound_view.id != view.id) { - flush(opts); - bound_view.* = view; - } } pub fn setDefaultSampler(sampler: Sampler) void { @@ -773,7 +748,17 @@ pub fn draw(opts: DrawOptions) void { return; } - bindView(view, .{}); + const view_state = sg.queryViewState(view); + if (view_state == .VALID or view_state == .ALLOC) { + const bound_view = getBoundView(); + if (bound_view.id != view.id) { + flush(); + bound_view.* = view; + } + } else { + log.warn("Attempt to bind non-valid view: state={}", .{view_state}); + return; + } const sampler = switch (opts.sampler orelse self.default_sampler) { .linear => self.linear_sampler, @@ -781,12 +766,12 @@ pub fn draw(opts: DrawOptions) void { }; const bound_sampler = &self.bindings.samplers[shd.SMP_smp]; if (bound_sampler.id != sampler.id) { - flush(.{}); + flush(); bound_sampler.* = sampler; } if (self.quads.items.len == self.quads.capacity) { - flush(.{}); + flush(); } if (getTransformPtr()) |transform| { @@ -843,6 +828,7 @@ const SetSpriteOptions = struct { pub fn setSprite(id: Sprite.Id, opts: SetSpriteOptions) void { const self = &g_state; + const sprite = self.sprites.get(id) orelse { log.warn("Attempt to set sprite that doesn't exist: {}", .{id}); return; @@ -897,12 +883,14 @@ pub fn setSprite(id: Sprite.Id, opts: SetSpriteOptions) void { const new_size = Vec2.initFromInt(u32, sprite_data.width, sprite_data.height); - if (old_size != null and old_size.?.eql(new_size) and sprite.position != null) { - spritesheet.needs_texture_rebuild = true; - } else { + log.debug("set sprite on {f}", .{sprite.spritesheet}); + + if (old_size == null or !old_size.?.eql(new_size) or sprite.position == null) { sprite.position = null; spritesheet.needs_repack = true; } + + spritesheet.needs_texture_rebuild = true; } // WARNING: This will change the UV coordinates of all sprites. @@ -915,7 +903,7 @@ fn repackSpritesheet(id: Spritesheet.Id) void { return; }; - log.debug("Repack spritesheet: {f}", .{id}); + log.debug("[{}] Repack spritesheet: {f}", .{self.frame_index, id}); // TODO: Add a smarter startegy for picking the initial texture size. // One idea is to calculate the sum area of all sprites and pick a atlas size based on that. @@ -994,7 +982,7 @@ fn rebuildSpritesheetTextureIfNeeded(id: Spritesheet.Id) void { return; } - log.debug("Rebuild spritesheet texture: {f}", .{id}); + log.debug("[{}] Rebuild spritesheet texture: {f}", .{self.frame_index, id}); const image_data = ImageData.init( self.gpa, @@ -1265,12 +1253,12 @@ pub fn getGlyph(id: Font.Id, index: GlyphIndex, scale_x: f32, scale_y: f32) ?*Fo }; glyph = font.cache.get(glyph_id); glyph.box = stb_font.getGlyphBitmapBox(glyph_key.index, glyph_key.scale_x, glyph_key.scale_y); + glyph.sprite = null; const box_width: u32 = @intCast(glyph.box.x1 - glyph.box.x0); const box_height: u32 = @intCast(glyph.box.y1 - glyph.box.y0); if (box_width > 0 and box_height > 0) { - assert(glyph.sprite == Sprite.Id.nil); - glyph.sprite = initSprite(.{ .spritesheet = font.spritesheet, .padding = 1 }); + const sprite = initSprite(.{ .spritesheet = font.spritesheet, .padding = 1 }); assert(box_width < Font.Glyph.max_width); assert(box_height < Font.Glyph.max_height); @@ -1288,9 +1276,10 @@ pub fn getGlyph(id: Font.Id, index: GlyphIndex, scale_x: f32, scale_y: f32) ?*Fo glyph_key.scale_x, glyph_key.scale_y, glyph_key.index ); - setSprite(glyph.sprite, .{ .image = bitmap }); - } else { - glyph.sprite = .nil; + setSprite(sprite, .{ .image = bitmap }); + + assert(glyph.sprite == null); + glyph.sprite = sprite; } } glyph.used_this_frame = true; @@ -1308,7 +1297,7 @@ pub const TextRunLayout = struct { const GlyphRect = struct { rect: Rect, - sprite: Sprite.Id, + sprite: ?Sprite.Id, }; pub fn init(font: Font.Id, height: f32) TextRunLayout { @@ -1406,9 +1395,10 @@ pub fn drawText(id: Font.Id, opts: DrawTextOptions) void { while (iter.nextCodepoint()) |codepoint| { const glyph_index = getGlyphIndex(id, codepoint) orelse continue; const glyph_layout = layout.next(glyph_index) orelse continue; + const glyph_sprite = glyph_layout.sprite orelse continue; drawSprite( - glyph_layout.sprite, + glyph_sprite, glyph_layout.rect.pos.add(pos), glyph_layout.rect.size, opts.color @@ -2133,7 +2123,7 @@ pub const Font = struct { key: Key, box: STBTrueType.Box, - sprite: Sprite.Id, + sprite: ?Sprite.Id, used_this_frame: bool, const max_width: u32 = 512; @@ -2169,7 +2159,9 @@ pub const Font = struct { } fn deinit(self: Glyph) void { - deinitSprite(self.sprite); + if (self.sprite) |sprite| { + deinitSprite(sprite); + } } }; }; diff --git a/src/platform/root.zig b/src/platform/root.zig index 88c709d..88fa967 100644 --- a/src/platform/root.zig +++ b/src/platform/root.zig @@ -139,7 +139,7 @@ fn PlatformType(App: type) type { try self.app.frame(plt); } - Gfx.flush(.{}); + Gfx.flush(); if (ImGUI.beginWindow(.{ .name = "Platform", .size = .init(300, 400)