208 lines
5.4 KiB
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;
|
|
}
|
|
};
|