sokoban-v1/engine/src/game_lib/asset.zig

208 lines
5.4 KiB
Zig

const std = @import("std");
const log = std.log.scoped(.engine);
const STBImage = @import("stb_image");
const Font = @import("./font.zig");
const Sound = @import("./sound.zig");
const Texture = @import("./texture.zig");
const Asset = @This();
id: Id,
kind: union(Type) {
font: Font.Id,
sound: Sound.Id,
texture: Texture.Id,
},
pub const Id = u32;
pub const Type = enum {
font,
sound,
texture
};
pub const Loader = struct {
const Descriptor = struct {
const KindTexture = struct {
path: []const u8,
info: Texture.Info
};
id: Id,
kind: union(Type) {
font: []const u8,
sound: []const u8,
texture: KindTexture
}
};
arena: std.heap.ArenaAllocator,
dir: std.fs.Dir,
assets: std.ArrayList(Descriptor),
add_failed: bool,
pub fn init(gpa: std.mem.Allocator, dir: std.fs.Dir) Loader {
return Loader{
.arena = .init(gpa),
.dir = dir,
.assets = .empty,
.add_failed = false
};
}
pub fn deinit(self: *Loader) void {
self.arena.deinit();
self.* = undefined;
}
fn readFile(self: *Loader, gpa: std.mem.Allocator, path: []const u8) ![]u8 {
const f = try self.dir.openFile(path, .{});
defer f.close();
var wa: std.Io.Writer.Allocating = .init(gpa);
defer wa.deinit();
var buffer: [4 * 4096]u8 = undefined;
var reader = f.reader(&buffer);
_ = try reader.interface.stream(&wa.writer, .unlimited);
return try wa.toOwnedSlice();
}
pub fn addFont(self: *Loader, id: Asset.Id, path: []const u8) void {
const allocator = self.arena.allocator();
const path_dupe = allocator.dupe(u8, path) catch |e| {
log.err("Failed to duplicate path: {}", .{e});
return;
};
self.assets.append(allocator, Descriptor{
.id = id,
.kind = .{ .font = path_dupe }
}) catch |e| {
log.err("Failed add sound: {}", .{e});
};
}
pub fn addSound(self: *Loader, id: Asset.Id, path: []const u8) void {
const allocator = self.arena.allocator();
const path_dupe = allocator.dupe(u8, path) catch |e| {
log.err("Failed to duplicate path: {}", .{e});
return;
};
self.assets.append(allocator, Descriptor{
.id = id,
.kind = .{ .sound = path_dupe }
}) catch |e| {
log.err("Failed add sound: {}", .{e});
};
}
fn addTextureFallible(self: *Loader, id: Asset.Id, path: []const u8) !void {
const allocator = self.arena.allocator();
var image_info: Texture.Info = undefined;
{
const file_contents = try self.readFile(allocator, path);
defer allocator.free(file_contents);
const image = try STBImage.load(file_contents);
defer image.deinit();
image_info = .{
.width = image.width,
.height = image.height,
.format = .png
};
}
const path_dupe = allocator.dupe(u8, path) catch |e| {
log.err("Failed to duplicate path: {}", .{e});
return;
};
const texture = Descriptor.KindTexture{
.path = path_dupe,
.info = image_info
};
self.assets.append(allocator, Descriptor{
.id = id,
.kind = .{ .texture = texture }
}) catch |e| {
log.err("Failed add sound: {}", .{e});
};
}
pub fn addTexture(self: *Loader, id: Asset.Id, path: []const u8) void {
self.addTextureFallible(id, path) catch |e| {
log.err("Failed to add texture: {}", .{e});
self.add_failed = true;
};
}
};
pub const Registry = struct {
assets: []const Asset,
textures: []const Texture,
pub const empty = Registry{
.assets = &.{},
.textures = &.{}
};
pub fn getAsset(self: Registry, id: Asset.Id) ?Asset {
// TODO: Make this O(1)?
for (self.assets) |asset| {
if (asset.id == id) {
return asset;
}
}
return null;
}
pub fn getSound(self: Registry, id: Asset.Id) Sound.Id {
const asset = self.getAsset(id) orelse return Sound.Id.nil;
if (asset.kind != .sound) {
return Sound.Id.nil;
}
return asset.kind.sound;
}
pub fn getFont(self: Registry, id: Asset.Id) Font.Id {
const asset = self.getAsset(id) orelse return Font.Id.nil;
if (asset.kind != .font) {
return Font.Id.nil;
}
return asset.kind.font;
}
pub fn getTexture(self: Registry, id: Asset.Id) Texture.Id {
const asset = self.getAsset(id) orelse return Texture.Id.nil;
if (asset.kind != .texture) {
return Texture.Id.nil;
}
return asset.kind.texture;
}
pub fn getTextureInfo(self: Registry, id: Texture.Id) Texture.Info {
if (id == .nil) {
log.warn("Failed to access .nil texture info", .{});
return .nil;
}
// TODO: Make this O(1)?
for (self.textures) |texture| {
if (texture.id == id) {
return texture.info;
}
}
log.warn("Failed to find texture info, id={}", .{id});
return .nil;
}
};