add initSpritesheet() and deinitSpritesheet()
This commit is contained in:
parent
c09ff2aad0
commit
ed560d3da1
@ -54,8 +54,6 @@ pub fn init(self: *App, plt: Platform.Init) !?u8 {
|
|||||||
.rect = Rect.init(1, 0, 1, 1).multiply(tile_size)
|
.rect = Rect.init(1, 0, 1, 1).multiply(tile_size)
|
||||||
});
|
});
|
||||||
|
|
||||||
try Gfx.packSprites();
|
|
||||||
|
|
||||||
self.* = App{
|
self.* = App{
|
||||||
.bg = .black,
|
.bg = .black,
|
||||||
.player_pos = .init(100, 100),
|
.player_pos = .init(100, 100),
|
||||||
|
|||||||
121
src/graphics.zig
121
src/graphics.zig
@ -28,6 +28,7 @@ const State = struct {
|
|||||||
|
|
||||||
default_sprite: Sprite.Id,
|
default_sprite: Sprite.Id,
|
||||||
error_sprite: Sprite.Id,
|
error_sprite: Sprite.Id,
|
||||||
|
default_spritesheet: Spritesheet.Id,
|
||||||
default_sampler: sg.Sampler,
|
default_sampler: sg.Sampler,
|
||||||
|
|
||||||
quads_buffer: [2048]Quad,
|
quads_buffer: [2048]Quad,
|
||||||
@ -38,9 +39,9 @@ const State = struct {
|
|||||||
textures_buffer: [Texture.max_textures]Texture.SlotMap.Slot,
|
textures_buffer: [Texture.max_textures]Texture.SlotMap.Slot,
|
||||||
textures: Texture.SlotMap,
|
textures: Texture.SlotMap,
|
||||||
|
|
||||||
spritesheet_texture: Texture.Id,
|
spritesheets_buffer: [Spritesheet.max_spritesheets]Spritesheet.SlotMap.Slot,
|
||||||
spritesheet_size: Vec2,
|
spritesheets: Spritesheet.SlotMap,
|
||||||
spritesheet_stale: bool,
|
|
||||||
sprites_buffer: [Sprite.max_sprites]Sprite.SlotMap.Slot,
|
sprites_buffer: [Sprite.max_sprites]Sprite.SlotMap.Slot,
|
||||||
sprites: Sprite.SlotMap,
|
sprites: Sprite.SlotMap,
|
||||||
};
|
};
|
||||||
@ -137,11 +138,11 @@ pub fn init(gpa: std.mem.Allocator, logger: sg.Logger) !void {
|
|||||||
.textures = .init(.initBuffer(&self.textures_buffer)),
|
.textures = .init(.initBuffer(&self.textures_buffer)),
|
||||||
.default_sprite = undefined,
|
.default_sprite = undefined,
|
||||||
.error_sprite = undefined,
|
.error_sprite = undefined,
|
||||||
.spritesheet_stale = false,
|
|
||||||
.sprites_buffer = undefined,
|
.sprites_buffer = undefined,
|
||||||
.sprites = .init(.initBuffer(&self.sprites_buffer)),
|
.sprites = .init(.initBuffer(&self.sprites_buffer)),
|
||||||
.spritesheet_texture = undefined,
|
.default_spritesheet = undefined,
|
||||||
.spritesheet_size = .init(0, 0)
|
.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 {
|
pub fn deinit() void {
|
||||||
@ -257,7 +258,8 @@ pub fn beginFrame() void {
|
|||||||
.swapchain = sglue.swapchain()
|
.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.views[shd.VIEW_tex] = spritesheet_texture.view;
|
||||||
self.bindings.samplers[shd.SMP_smp] = self.default_sampler;
|
self.bindings.samplers[shd.SMP_smp] = self.default_sampler;
|
||||||
}
|
}
|
||||||
@ -432,9 +434,12 @@ pub fn draw(opts: DrawOptions) void {
|
|||||||
var texture = opts.texture;
|
var texture = opts.texture;
|
||||||
if (!self.textures.exists(texture)) {
|
if (!self.textures.exists(texture)) {
|
||||||
if (getSpriteUVRect(self.error_sprite)) |error_uv| {
|
if (getSpriteUVRect(self.error_sprite)) |error_uv| {
|
||||||
texture = self.spritesheet_texture;
|
|
||||||
quad.setUVRect(error_uv);
|
quad.setUVRect(error_uv);
|
||||||
quad.setColor(.white);
|
quad.setColor(.white);
|
||||||
|
|
||||||
|
const sprite = self.sprites.getAssumeExists(self.error_sprite);
|
||||||
|
const spritesheet = self.spritesheets.getAssumeExists(sprite.spritesheet);
|
||||||
|
texture = spritesheet.texture;
|
||||||
} else {
|
} else {
|
||||||
log.err("Failed to draw error texture", .{});
|
log.err("Failed to draw error texture", .{});
|
||||||
return;
|
return;
|
||||||
@ -462,6 +467,7 @@ pub fn initSprite() !Sprite.Id {
|
|||||||
|
|
||||||
const sprite = self.sprites.getAssumeExists(id);
|
const sprite = self.sprites.getAssumeExists(id);
|
||||||
sprite.* = .{
|
sprite.* = .{
|
||||||
|
.spritesheet = self.default_spritesheet,
|
||||||
.data = null,
|
.data = null,
|
||||||
.position = null
|
.position = null
|
||||||
};
|
};
|
||||||
@ -477,7 +483,11 @@ pub fn deinitSprite(id: Sprite.Id) void {
|
|||||||
data.deinit(self.gpa);
|
data.deinit(self.gpa);
|
||||||
}
|
}
|
||||||
self.sprites.removeAssumeExists(id);
|
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.data = sprite_data;
|
||||||
sprite.position = null;
|
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 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.
|
// 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.
|
// 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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const spritesheet = try ImageData.initRGBA8(self.gpa, texture_size, texture_size);
|
const image_data = try ImageData.initRGBA8(self.gpa, texture_size, texture_size);
|
||||||
defer spritesheet.deinit(self.gpa);
|
defer image_data.deinit(self.gpa);
|
||||||
|
|
||||||
{
|
{
|
||||||
var i: usize = 0;
|
var i: usize = 0;
|
||||||
@ -578,7 +598,7 @@ pub fn packSprites() !void {
|
|||||||
const x: u32 = @intCast(rects[i].x);
|
const x: u32 = @intCast(rects[i].x);
|
||||||
const y: u32 = @intCast(rects[i].y);
|
const y: u32 = @intCast(rects[i].y);
|
||||||
sprite.position = .initFromInt(u32, x, y);
|
sprite.position = .initFromInt(u32, x, y);
|
||||||
spritesheet.copyFrom(
|
image_data.copyFrom(
|
||||||
sprite_data,
|
sprite_data,
|
||||||
x,
|
x,
|
||||||
y,
|
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);
|
spritesheet.size = .initFromInt(u32, texture_size, texture_size);
|
||||||
self.spritesheet_stale = false;
|
spritesheet.stale = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -613,18 +633,19 @@ pub fn getSpriteUVRect(id: Sprite.Id) ?Rect {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (self.spritesheet_stale) {
|
const spritesheet = self.spritesheets.get(sprite.spritesheet) orelse return null;
|
||||||
packSprites() catch |e| {
|
|
||||||
|
packSprites(sprite.spritesheet) catch |e| {
|
||||||
log.warn("Failed to pack sprites: {}", .{e});
|
log.warn("Failed to pack sprites: {}", .{e});
|
||||||
|
return null;
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
assert(sprite.position != 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{
|
return Rect{
|
||||||
.pos = sprite.position.?.divide(self.spritesheet_size),
|
.pos = sprite.position.?.divide(spritesheet.size),
|
||||||
.size = sprite_size.divide(self.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.setColor(color);
|
||||||
quad.setRect(.{ .pos = pos, .size = size });
|
quad.setRect(.{ .pos = pos, .size = size });
|
||||||
|
|
||||||
|
var texture: ?Texture.Id = null;
|
||||||
if (getSpriteUVRect(id)) |sprite_uv| {
|
if (getSpriteUVRect(id)) |sprite_uv| {
|
||||||
quad.setUVRect(sprite_uv);
|
quad.setUVRect(sprite_uv);
|
||||||
} else if (getSpriteUVRect(self.error_sprite)) |error_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.setUVRect(error_uv);
|
||||||
quad.setColor(.white);
|
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(.{
|
draw(.{
|
||||||
.texture = self.spritesheet_texture,
|
.texture = texture.?,
|
||||||
.quad = quad
|
.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 {
|
pub const ImageData = struct {
|
||||||
const Pixels = union(enum) {
|
const Pixels = union(enum) {
|
||||||
none,
|
none,
|
||||||
@ -755,6 +815,7 @@ pub const Texture = struct {
|
|||||||
};
|
};
|
||||||
|
|
||||||
pub const Sprite = struct {
|
pub const Sprite = struct {
|
||||||
|
spritesheet: Spritesheet.Id,
|
||||||
data: ?ImageData,
|
data: ?ImageData,
|
||||||
position: ?Vec2,
|
position: ?Vec2,
|
||||||
|
|
||||||
@ -763,6 +824,16 @@ pub const Sprite = struct {
|
|||||||
pub const Id = SlotMap.Id;
|
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 {
|
pub const Vertex = extern struct {
|
||||||
position: Vec2,
|
position: Vec2,
|
||||||
texcoord: Vec2,
|
texcoord: Vec2,
|
||||||
|
|||||||
@ -51,7 +51,7 @@ pub fn SlotMapType(Index: type, Generation: type, Value: type) type {
|
|||||||
index: Index,
|
index: Index,
|
||||||
|
|
||||||
pub fn format(self: Id, writer: *std.Io.Writer) std.Io.Writer.Error!void {
|
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;
|
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 {
|
pub fn getAssumeExists(self: *Self, id: Id) *Value {
|
||||||
assert(self.exists(id));
|
assert(self.exists(id));
|
||||||
return &self.slots.items[id.index].value;
|
return self.get(id).?;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn removeAssumeExists(self: *Self, id: Id) void {
|
pub fn removeAssumeExists(self: *Self, id: Id) void {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user