diff --git a/src/app.zig b/src/app.zig index e663944..694313a 100644 --- a/src/app.zig +++ b/src/app.zig @@ -42,13 +42,13 @@ pub fn init(self: *App, plt: Platform.Init) !?u8 { const tile_size = Vec2.init(64, 64); - const player_sprite = try Gfx.initSprite(); + const player_sprite = Gfx.initSprite(); Gfx.setSprite(player_sprite, .{ .image = spritesheet, .rect = Rect.init(0, 4, 1, 1).multiply(tile_size) }); - const wall_sprite = try Gfx.initSprite(); + const wall_sprite = Gfx.initSprite(); Gfx.setSprite(wall_sprite, .{ .image = spritesheet, .rect = Rect.init(1, 0, 1, 1).multiply(tile_size) diff --git a/src/graphics.zig b/src/graphics.zig index 626af77..c15757d 100644 --- a/src/graphics.zig +++ b/src/graphics.zig @@ -38,12 +38,15 @@ const State = struct { textures_buffer: [Texture.max_textures]Texture.SlotMap.Slot, textures: Texture.SlotMap, + nil_texture: Texture.Id, spritesheets_buffer: [Spritesheet.max_spritesheets]Spritesheet.SlotMap.Slot, spritesheets: Spritesheet.SlotMap, + nil_spritesheet: Spritesheet.Id, sprites_buffer: [Sprite.max_sprites]Sprite.SlotMap.Slot, sprites: Sprite.SlotMap, + nil_sprite: Sprite.Id, }; var g_state: State = undefined; @@ -134,17 +137,52 @@ pub fn init(gpa: std.mem.Allocator, logger: sg.Logger) !void { .quads_buffer = undefined, .quads = .initBuffer(&self.quads_buffer), .clear_color = .black, - .textures_buffer = undefined, - .textures = .init(.initBuffer(&self.textures_buffer)), .default_sprite = undefined, .error_sprite = undefined, + .default_spritesheet = undefined, + + .textures_buffer = undefined, + .textures = .init(.initBuffer(&self.textures_buffer)), + .nil_texture = undefined, + .sprites_buffer = undefined, .sprites = .init(.initBuffer(&self.sprites_buffer)), - .default_spritesheet = undefined, + .nil_sprite = undefined, + .spritesheets_buffer = undefined, - .spritesheets = .init(.initBuffer(&self.spritesheets_buffer)) + .spritesheets = .init(.initBuffer(&self.spritesheets_buffer)), + .nil_spritesheet = undefined, }; + self.nil_texture = self.textures.insertAssumeCapacity(); + { + const nil_texture = self.textures.getAssumeExists(self.nil_texture); + nil_texture.* = Texture{ + .image = .{ .id = sg.invalid_id }, + .view = .{ .id = sg.invalid_id }, + }; + } + + self.nil_spritesheet = self.spritesheets.insertAssumeCapacity(); + { + const nil_spritesheet = self.spritesheets.getAssumeExists(self.nil_spritesheet); + nil_spritesheet.* = Spritesheet{ + .texture = self.nil_texture, + .size = .init(0, 0), + .stale = false + }; + } + + self.nil_sprite = self.sprites.insertAssumeCapacity(); + { + const nil_sprite = self.sprites.getAssumeExists(self.nil_sprite); + nil_sprite.* = Sprite{ + .spritesheet = self.nil_spritesheet, + .data = .none, + .position = null + }; + } + { const width = 8; const height = 8; @@ -152,7 +190,7 @@ pub fn init(gpa: std.mem.Allocator, logger: sg.Logger) !void { var pixels: [width * height * 4]u8 = undefined; @memset(&pixels, 0xFF); - self.default_sprite = try initSprite(); + self.default_sprite = initSprite(); setSprite(self.default_sprite, .{ .image = ImageData{ .width = width, @@ -186,7 +224,7 @@ pub fn init(gpa: std.mem.Allocator, logger: sg.Logger) !void { } } - self.error_sprite = try initSprite(); + self.error_sprite = initSprite(); setSprite(self.error_sprite, .{ .image = .{ .width = width, @@ -196,7 +234,7 @@ pub fn init(gpa: std.mem.Allocator, logger: sg.Logger) !void { }); } - self.default_spritesheet = try initSpritesheet(); + self.default_spritesheet = initSpritesheet(); } pub fn deinit() void { @@ -343,15 +381,24 @@ pub fn drawLine(from: Vec2, to: Vec2, color: Vec4, width: f32) void { }); } -pub fn initTexture() !Texture.Id { +pub fn initTexture() Texture.Id { const self = &g_state; - const id = try self.textures.insertBounded(); + const id = self.textures.insertBounded() catch { + log.warn("Failed to create texture, limit reached! limit: {}", .{self.textures.slots.capacity}); + return self.nil_texture; + }; const image = sg.allocImage(); - assert(image.id != sg.invalid_id); + if (image.id == sg.invalid_id) { + log.warn("allocImage() failed!", .{}); + return self.nil_texture; + } const view = sg.allocView(); - assert(view.id != sg.invalid_id); + if (view.id == sg.invalid_id) { + log.warn("allocView() failed!", .{}); + return self.nil_texture; + } const texture = self.textures.getAssumeExists(id); texture.* = .{ @@ -363,6 +410,10 @@ pub fn initTexture() !Texture.Id { pub fn deinitTexture(id: Texture.Id) void { const self = &g_state; + if (id == self.nil_texture) { + return; + } + if (self.textures.remove(id)) { const texture = self.textures_buffer[id.index]; const bound_view = &self.bindings.views[shd.VIEW_tex]; @@ -378,11 +429,14 @@ pub fn deinitTexture(id: Texture.Id) void { pub fn setTexture(id: Texture.Id, texture_data: ImageData) void { const self = &g_state; - if (!self.textures.exists(id)) { - log.warn("Attempt to set data for texture that doesn't exist: {}", .{id}); + if (id == self.nil_texture) { return; } - const texture = self.textures.getAssumeExists(id); + + const texture = self.textures.get(id) orelse { + log.warn("Attempt to set texture that doesn't exist: {}", .{id}); + return; + }; if (sg.queryImageState(texture.image) == .VALID) { sg.uninitImage(texture.image); @@ -400,13 +454,19 @@ pub fn setTexture(id: Texture.Id, texture_data: ImageData) void { .pixel_format = .RGBA8, .data = image_data, }); - assert(sg.queryImageState(texture.image) == .VALID); + if (sg.queryImageState(texture.image) != .VALID) { + log.warn("initImage() failed", .{}); + return; + } if (sg.queryViewState(texture.view) == .ALLOC) { sg.initView(texture.view, .{ .texture = .{ .image = texture.image } }); - assert(sg.queryViewState(texture.view) == .VALID); + if (sg.queryViewState(texture.view) != .VALID) { + log.warn("initView() failed", .{}); + return; + } } } @@ -431,22 +491,31 @@ pub fn draw(opts: DrawOptions) void { const self = &g_state; var quad = opts.quad; - var texture = opts.texture; - if (!self.textures.exists(texture)) { - if (getSpriteUVRect(self.error_sprite)) |error_uv| { - quad.setUVRect(error_uv); - quad.setColor(.white); - const sprite = self.sprites.getAssumeExists(self.error_sprite); - const spritesheet = self.spritesheets.getAssumeExists(sprite.spritesheet); - texture = spritesheet.texture; - } else { - log.err("Failed to draw error texture", .{}); - return; + var view: sg.View = .{ .id = sg.invalid_id }; + if (opts.texture != self.nil_texture and self.textures.exists(opts.texture)) { + const texture = self.textures.getAssumeExists(opts.texture); + view = texture.view; + } + + if (sg.queryViewState(view) != .VALID) { + if (self.sprites.get(self.error_sprite)) |sprite| { + if (self.spritesheets.get(sprite.spritesheet)) |spritesheet| { + if (self.textures.get(spritesheet.texture)) |texture| { + if (getSpriteUVRect(self.error_sprite)) |uv| { + quad.setUVRect(uv); + quad.setColor(.white); + view = texture.view; + } + } + } } } - const view = self.textures.getAssumeExists(texture).view; + if (sg.queryViewState(view) != .VALID) { + log.warn("Failed to draw error image", .{}); + return; + } const bound_view = &self.bindings.views[shd.VIEW_tex]; if (bound_view.id != view.id) { @@ -461,9 +530,12 @@ pub fn draw(opts: DrawOptions) void { self.quads.appendAssumeCapacity(quad); } -pub fn initSprite() !Sprite.Id { +pub fn initSprite() Sprite.Id { const self = &g_state; - const id = try self.sprites.insertBounded(); + const id = self.sprites.insertBounded() catch { + log.warn("Failed to create sprite, limit reached! limit: {}", .{self.sprites.slots.capacity}); + return self.nil_sprite; + }; const sprite = self.sprites.getAssumeExists(id); sprite.* = .{ @@ -477,6 +549,10 @@ pub fn initSprite() !Sprite.Id { pub fn deinitSprite(id: Sprite.Id) void { const self = &g_state; + if (id == self.nil_sprite) { + return; + } + if (self.sprites.exists(id)) { const sprite = self.sprites.getAssumeExists(id); if (sprite.data) |data| { @@ -484,7 +560,6 @@ pub fn deinitSprite(id: Sprite.Id) void { } self.sprites.removeAssumeExists(id); - if (self.spritesheets.get(sprite.spritesheet)) |spritesheet| { spritesheet.stale = true; } @@ -498,12 +573,16 @@ const SetSpriteOptions = struct { pub fn setSprite(id: Sprite.Id, opts: SetSpriteOptions) void { const self = &g_state; - if (!self.sprites.exists(id)) { - log.warn("Attempt to set data for sprite that doesn't exist: {}", .{id}); + if (id == self.nil_sprite) { return; } - var sprite_data: ImageData = .empty; + const sprite = self.sprites.get(id) orelse { + log.warn("Attempt to set sprite that doesn't exist: {}", .{id}); + return; + }; + + var sprite_data: ImageData = .none; errdefer sprite_data.deinit(self.gpa); if (opts.rect) |rect| { @@ -525,7 +604,6 @@ pub fn setSprite(id: Sprite.Id, opts: SetSpriteOptions) void { }; } - const sprite = self.sprites.getAssumeExists(id); if (sprite.data) |old_sprite_data| { old_sprite_data.deinit(self.gpa); } @@ -537,8 +615,12 @@ pub fn setSprite(id: Sprite.Id, opts: SetSpriteOptions) void { } } -pub fn packSprites(id: Spritesheet.Id) !void { +pub fn packSpritesheet(id: Spritesheet.Id) !void { const self = &g_state; + if (id == self.nil_spritesheet) { + return; + } + const spritesheet = self.spritesheets.get(id) orelse { log.warn("Attempt to pack spritesheet that doesn't exist: {}", .{id}); return; @@ -555,29 +637,33 @@ pub fn packSprites(id: Spritesheet.Id) !void { }; var nodes: [32]STBRectPack.Node = undefined; + + var sprites_buffer: [Sprite.max_sprites]*Sprite = undefined; + var sprites = std.ArrayList(*Sprite).initBuffer(&sprites_buffer); + + var sprite_iter = self.sprites.iterator(); + while (sprite_iter.next()) |sprite_id| { + const sprite = self.sprites.getAssumeExists(sprite_id); + if (sprite.data != null and sprite.spritesheet == id) { + sprites.appendAssumeCapacity(sprite); + } + } + var rects: [Sprite.max_sprites]STBRectPack.Rect = undefined; + @memset(&rects, .{}); + for (0.., sprites.items) |i, sprite| { + const sprite_data = sprite.data.?; + rects[i] = .{ + .w = @intCast(sprite_data.width), + .h = @intCast(sprite_data.height), + }; + } for (&texture_sizes) |texture_size| { var ctx: STBRectPack = undefined; ctx.init(texture_size, texture_size, &nodes); ctx.setupAllowOutOfMem(true); - @memset(&rects, .{}); - - - { - var i: usize = 0; - var sprite_iter = self.sprites.iterator(); - while (sprite_iter.next()) |sprite_id| : (i += 1) { - const sprite = self.sprites.getAssumeExists(sprite_id); - const sprite_data = sprite.data orelse continue; - rects[i] = .{ - .w = @intCast(sprite_data.width), - .h = @intCast(sprite_data.height), - }; - } - } - const success = ctx.packRects(&rects); std.debug.print("success {} {}\n", .{texture_size, success}); if (!success) { @@ -587,27 +673,21 @@ pub fn packSprites(id: Spritesheet.Id) !void { const image_data = try ImageData.initRGBA8(self.gpa, texture_size, texture_size); defer image_data.deinit(self.gpa); - { - var i: usize = 0; - var sprite_iter = self.sprites.iterator(); - while (sprite_iter.next()) |sprite_id| : (i += 1) { - const sprite = self.sprites.getAssumeExists(sprite_id); - const sprite_data = sprite.data orelse continue; - - assert(rects[i].was_packed == 1); - const x: u32 = @intCast(rects[i].x); - const y: u32 = @intCast(rects[i].y); - sprite.position = .initFromInt(u32, x, y); - image_data.copyFrom( - sprite_data, - x, - y, - 0, - 0, - sprite_data.width, - sprite_data.height - ); - } + for (0.., sprites.items) |i, sprite| { + const sprite_data = sprite.data.?; + assert(rects[i].was_packed == 1); + const x: u32 = @intCast(rects[i].x); + const y: u32 = @intCast(rects[i].y); + sprite.position = .initFromInt(u32, x, y); + image_data.copyFrom( + sprite_data, + x, + y, + 0, + 0, + sprite_data.width, + sprite_data.height + ); } setTexture(spritesheet.texture, image_data); @@ -617,32 +697,30 @@ pub fn packSprites(id: Spritesheet.Id) !void { return; } - const max_texture_size = texture_sizes[texture_sizes.len-1]; log.warn("Failed to pack sprites, couldn't create a texture that is large enough. Max size tried: {}x{}", .{max_texture_size, max_texture_size}); + return error.TooManySprites; } pub fn getSpriteUVRect(id: Sprite.Id) ?Rect { const self = &g_state; + if (id == self.nil_sprite) { + return null; + } - if (!self.sprites.exists(id)) { - return null; - } - const sprite = self.sprites.getAssumeExists(id); - if (sprite.data == null) { - return null; - } + const sprite = self.sprites.get(id) orelse return null; + const sprite_data = sprite.data orelse return null; const spritesheet = self.spritesheets.get(sprite.spritesheet) orelse return null; - packSprites(sprite.spritesheet) catch |e| { + packSpritesheet(sprite.spritesheet) catch |e| { log.warn("Failed to pack sprites: {}", .{e}); return null; }; assert(sprite.position != null); - const sprite_size = Vec2.initFromInt(u32, sprite.data.?.width, sprite.data.?.height); + const sprite_size = Vec2.initFromInt(u32, sprite_data.width, sprite_data.height); return Rect{ .pos = sprite.position.?.divide(spritesheet.size), .size = sprite_size.divide(spritesheet.size), @@ -657,46 +735,33 @@ pub fn drawSprite(id: Sprite.Id, pos: Vec2, size: Vec2, color: Color) void { quad.setRect(.{ .pos = pos, .size = size }); var texture: ?Texture.Id = null; - if (getSpriteUVRect(id)) |sprite_uv| { - quad.setUVRect(sprite_uv); - if (self.sprites.get(id)) |sprite| { - if (self.spritesheets.get(sprite.spritesheet)) |spritesheet| { - texture = spritesheet.texture; + if (id != self.nil_sprite) { + if (getSpriteUVRect(id)) |sprite_uv| { + quad.setUVRect(sprite_uv); + if (self.sprites.get(id)) |sprite| { + if (self.spritesheets.get(sprite.spritesheet)) |spritesheet| { + texture = spritesheet.texture; + } } } - - } - - if (texture == null) { - if (getSpriteUVRect(self.error_sprite)) |error_uv| { - quad.setUVRect(error_uv); - quad.setColor(.white); - - const sprite = self.sprites.getAssumeExists(self.error_sprite); - const spritesheet = self.spritesheets.getAssumeExists(sprite.spritesheet); - texture = spritesheet.texture; - } - } - - if (texture == null) { - log.warn("Failed to draw sprite", .{}); - return; } draw(.{ - .texture = texture.?, + .texture = texture orelse self.nil_texture, .quad = quad }); } -pub fn initSpritesheet() !Spritesheet.Id { - // TODO: self.spritesheet_texture = try initTexture(); +pub fn initSpritesheet() Spritesheet.Id { const self = &g_state; - const id = try self.spritesheets.insertBounded(); + const id = self.spritesheets.insertBounded() catch { + log.warn("Failed to create spritesheet, limit reached! limit: {}", .{self.spritesheets.slots.capacity}); + return self.nil_spritesheet; + }; const spritesheet = self.spritesheets.getAssumeExists(id); spritesheet.* = .{ - .texture = try initTexture(), + .texture = initTexture(), .size = .init(0, 0), .stale = false, }; @@ -705,7 +770,16 @@ pub fn initSpritesheet() !Spritesheet.Id { } pub fn deinitSpritesheet(id: Spritesheet.Id) void { - _ = id; // autofix + const self = &g_state; + if (id == self.nil_spritesheet) { + return; + } + + if (self.spritesheets.get(id)) { + const spritesheet = self.spritesheets.getAssumeExists(id); + deinitTexture(spritesheet.texture); + self.spritesheets.removeAssumeExists(id); + } } pub const ImageData = struct { @@ -718,7 +792,7 @@ pub const ImageData = struct { width: u32, height: u32, - pub const empty = ImageData{ + pub const none = ImageData{ .pixels = .none, .width = 0, .height = 0, diff --git a/src/platform.zig b/src/platform.zig index d92a049..d09dfdd 100644 --- a/src/platform.zig +++ b/src/platform.zig @@ -281,18 +281,38 @@ fn PlatformType(App: type) type { } } + const input = &self.input; + switch (e.type) { .MOUSE_DOWN => { + if (input.mouse_position == null) { + self.pushEvent(.{ + .mouse_enter = .init(e.mouse_x, e.mouse_y) + }); + } + self.pushEvent(.{ .mouse_pressed = Input.MouseButton.fromSokol(e.mouse_button) orelse return false, }); }, .MOUSE_UP => { + if (input.mouse_position == null) { + self.pushEvent(.{ + .mouse_enter = .init(e.mouse_x, e.mouse_y) + }); + } + self.pushEvent(.{ .mouse_released = Input.MouseButton.fromSokol(e.mouse_button) orelse return false, }); }, .MOUSE_MOVE => { + if (input.mouse_position == null) { + self.pushEvent(.{ + .mouse_enter = .init(e.mouse_x, e.mouse_y) + }); + } + self.pushEvent(.{ .mouse_move = .init(e.mouse_x, e.mouse_y) });