add initSpritesheet() and deinitSpritesheet()

This commit is contained in:
Rokas Puzonas 2026-07-11 17:40:21 +03:00
parent c09ff2aad0
commit ed560d3da1
3 changed files with 109 additions and 33 deletions

View File

@ -54,8 +54,6 @@ pub fn init(self: *App, plt: Platform.Init) !?u8 {
.rect = Rect.init(1, 0, 1, 1).multiply(tile_size)
});
try Gfx.packSprites();
self.* = App{
.bg = .black,
.player_pos = .init(100, 100),

View File

@ -28,6 +28,7 @@ const State = struct {
default_sprite: Sprite.Id,
error_sprite: Sprite.Id,
default_spritesheet: Spritesheet.Id,
default_sampler: sg.Sampler,
quads_buffer: [2048]Quad,
@ -38,9 +39,9 @@ const State = struct {
textures_buffer: [Texture.max_textures]Texture.SlotMap.Slot,
textures: Texture.SlotMap,
spritesheet_texture: Texture.Id,
spritesheet_size: Vec2,
spritesheet_stale: bool,
spritesheets_buffer: [Spritesheet.max_spritesheets]Spritesheet.SlotMap.Slot,
spritesheets: Spritesheet.SlotMap,
sprites_buffer: [Sprite.max_sprites]Sprite.SlotMap.Slot,
sprites: Sprite.SlotMap,
};
@ -137,11 +138,11 @@ pub fn init(gpa: std.mem.Allocator, logger: sg.Logger) !void {
.textures = .init(.initBuffer(&self.textures_buffer)),
.default_sprite = undefined,
.error_sprite = undefined,
.spritesheet_stale = false,
.sprites_buffer = undefined,
.sprites = .init(.initBuffer(&self.sprites_buffer)),
.spritesheet_texture = undefined,
.spritesheet_size = .init(0, 0)
.default_spritesheet = undefined,
.spritesheets_buffer = undefined,
.spritesheets = .init(.initBuffer(&self.spritesheets_buffer))
};
{
@ -195,7 +196,7 @@ pub fn init(gpa: std.mem.Allocator, logger: sg.Logger) !void {
});
}
self.spritesheet_texture = try initTexture();
self.default_spritesheet = try initSpritesheet();
}
pub fn deinit() void {
@ -257,7 +258,8 @@ pub fn beginFrame() void {
.swapchain = sglue.swapchain()
});
const spritesheet_texture = self.textures.getAssumeExists(self.spritesheet_texture);
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.samplers[shd.SMP_smp] = self.default_sampler;
}
@ -432,9 +434,12 @@ pub fn draw(opts: DrawOptions) void {
var texture = opts.texture;
if (!self.textures.exists(texture)) {
if (getSpriteUVRect(self.error_sprite)) |error_uv| {
texture = self.spritesheet_texture;
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;
@ -462,6 +467,7 @@ pub fn initSprite() !Sprite.Id {
const sprite = self.sprites.getAssumeExists(id);
sprite.* = .{
.spritesheet = self.default_spritesheet,
.data = null,
.position = null
};
@ -477,7 +483,11 @@ pub fn deinitSprite(id: Sprite.Id) void {
data.deinit(self.gpa);
}
self.sprites.removeAssumeExists(id);
self.spritesheet_stale = true;
if (self.spritesheets.get(sprite.spritesheet)) |spritesheet| {
spritesheet.stale = true;
}
}
}
@ -522,11 +532,21 @@ pub fn setSprite(id: Sprite.Id, opts: SetSpriteOptions) void {
sprite.data = sprite_data;
sprite.position = null;
self.spritesheet_stale = true;
if (self.spritesheets.get(sprite.spritesheet)) |spritesheet| {
spritesheet.stale = true;
}
}
pub fn packSprites() !void {
pub fn packSprites(id: Spritesheet.Id) !void {
const self = &g_state;
const spritesheet = self.spritesheets.get(id) orelse {
log.warn("Attempt to pack spritesheet that doesn't exist: {}", .{id});
return;
};
if (!spritesheet.stale) {
return;
}
// 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.
@ -564,8 +584,8 @@ pub fn packSprites() !void {
continue;
}
const spritesheet = try ImageData.initRGBA8(self.gpa, texture_size, texture_size);
defer spritesheet.deinit(self.gpa);
const image_data = try ImageData.initRGBA8(self.gpa, texture_size, texture_size);
defer image_data.deinit(self.gpa);
{
var i: usize = 0;
@ -578,7 +598,7 @@ pub fn packSprites() !void {
const x: u32 = @intCast(rects[i].x);
const y: u32 = @intCast(rects[i].y);
sprite.position = .initFromInt(u32, x, y);
spritesheet.copyFrom(
image_data.copyFrom(
sprite_data,
x,
y,
@ -590,10 +610,10 @@ pub fn packSprites() !void {
}
}
setTexture(self.spritesheet_texture, spritesheet);
setTexture(spritesheet.texture, image_data);
self.spritesheet_size = .initFromInt(u32, texture_size, texture_size);
self.spritesheet_stale = false;
spritesheet.size = .initFromInt(u32, texture_size, texture_size);
spritesheet.stale = false;
return;
}
@ -613,18 +633,19 @@ pub fn getSpriteUVRect(id: Sprite.Id) ?Rect {
return null;
}
if (self.spritesheet_stale) {
packSprites() catch |e| {
log.warn("Failed to pack sprites: {}", .{e});
};
}
const spritesheet = self.spritesheets.get(sprite.spritesheet) orelse return null;
packSprites(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);
return Rect{
.pos = sprite.position.?.divide(self.spritesheet_size),
.size = sprite_size.divide(self.spritesheet_size),
.pos = sprite.position.?.divide(spritesheet.size),
.size = sprite_size.divide(spritesheet.size),
};
}
@ -635,19 +656,58 @@ pub fn drawSprite(id: Sprite.Id, pos: Vec2, size: Vec2, color: Color) void {
quad.setColor(color);
quad.setRect(.{ .pos = pos, .size = size });
var texture: ?Texture.Id = null;
if (getSpriteUVRect(id)) |sprite_uv| {
quad.setUVRect(sprite_uv);
} else if (getSpriteUVRect(self.error_sprite)) |error_uv| {
quad.setUVRect(error_uv);
quad.setColor(.white);
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 = self.spritesheet_texture,
.texture = texture.?,
.quad = quad
});
}
pub fn initSpritesheet() !Spritesheet.Id {
// TODO: self.spritesheet_texture = try initTexture();
const self = &g_state;
const id = try self.spritesheets.insertBounded();
const spritesheet = self.spritesheets.getAssumeExists(id);
spritesheet.* = .{
.texture = try initTexture(),
.size = .init(0, 0),
.stale = false,
};
return id;
}
pub fn deinitSpritesheet(id: Spritesheet.Id) void {
_ = id; // autofix
}
pub const ImageData = struct {
const Pixels = union(enum) {
none,
@ -755,6 +815,7 @@ pub const Texture = struct {
};
pub const Sprite = struct {
spritesheet: Spritesheet.Id,
data: ?ImageData,
position: ?Vec2,
@ -763,6 +824,16 @@ pub const Sprite = struct {
pub const Id = SlotMap.Id;
};
pub const Spritesheet = struct {
texture: Texture.Id,
size: Vec2,
stale: bool,
const max_spritesheets = 64;
const SlotMap = SlotMapType(std.math.IntFittingRange(0, max_spritesheets-1), u8, Spritesheet);
pub const Id = SlotMap.Id;
};
pub const Vertex = extern struct {
position: Vec2,
texcoord: Vec2,

View File

@ -51,7 +51,7 @@ pub fn SlotMapType(Index: type, Generation: type, Value: type) type {
index: Index,
pub fn format(self: Id, writer: *std.Io.Writer) std.Io.Writer.Error!void {
try writer.print("Id{{ {}, {} }}", .{ self.index, self.generation });
try writer.print("Id({s}){{ {}, {} }}", .{ @typeName(Value), self.index, self.generation });
}
};
@ -173,9 +173,16 @@ pub fn SlotMapType(Index: type, Generation: type, Value: type) type {
return slot.used and slot.generation == id.generation;
}
pub fn get(self: *Self, id: Id) ?*Value {
if (self.exists(id)) {
return &self.slots.items[id.index].value;
}
return null;
}
pub fn getAssumeExists(self: *Self, id: Id) *Value {
assert(self.exists(id));
return &self.slots.items[id.index].value;
return self.get(id).?;
}
pub fn removeAssumeExists(self: *Self, id: Id) void {