Files
voxel-game/src/asset_pipeline.zig

164 lines
5.1 KiB
Zig
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const std = @import("std");
const main = @import("main.zig");
const zstbi = @import("zstbi");
pub const Texture = struct {
width: u32,
height: u32,
depth: u32,
data: []u8,
pub fn deinit(self: *Texture, allocator: std.mem.Allocator) void {
allocator.free(self.data);
self.* = undefined;
}
};
pub const AssetMap = std.hash_map.StringHashMapUnmanaged;
pub fn visitTextures(allocator: std.mem.Allocator) AssetMap(Texture) {
zstbi.init(allocator);
defer zstbi.deinit();
const cwd = std.fs.cwd();
const sub_path = "assets/textures";
var ret: AssetMap(Texture) = .empty;
var textures_dir = cwd.openDir(sub_path, .{ .iterate = true }) catch |err| {
std.log.err("Could not open \"{s}\" directory: {s}", .{ sub_path, @errorName(err) });
return ret;
};
defer textures_dir.close();
var it = textures_dir.iterate();
while (it.next() catch |err| blk: {
std.log.err("Directory iteration interrupted due to an error: {s}", .{@errorName(err)});
break :blk null;
}) |entry| {
if (entry.kind != .file) {
std.log.warn("Skipping \"{s}\", which is not a file.", .{entry.name});
continue;
}
var texture = visitTexture(allocator, textures_dir, entry.name) catch {
continue;
};
const name = allocator.dupe(u8, std.fs.path.stem(entry.name)) catch {
std.log.err("Ran out of memory while trying to allocate asset name", .{});
texture.deinit(allocator);
continue;
};
ret.putNoClobber(allocator, name, texture) catch {
std.log.err("Ran out of memory while trying to save asset to map", .{});
texture.deinit(allocator);
allocator.free(name);
continue;
};
}
return ret;
}
pub fn visitTexture(allocator: std.mem.Allocator, dir: std.fs.Dir, filename: []const u8) !Texture {
std.log.info("Processing \"{s}\"...", .{filename});
const file = dir.openFile(filename, .{}) catch |err| {
std.log.err("Could not open \"{s}\" file: {s}", .{ filename, @errorName(err) });
return err;
};
defer file.close();
const file_buf = file.readToEndAlloc(allocator, std.math.maxInt(usize)) catch |err| {
std.log.err("Could not read \"{s}\" file contents due to an error: {s}", .{ filename, @errorName(err) });
return err;
};
defer allocator.free(file_buf);
var img = zstbi.Image.loadFromMemory(file_buf, 4) catch |err| {
std.log.err("Error reading \"{s}\" as an image file: {s}", .{ filename, @errorName(err) });
return err;
};
defer img.deinit();
std.log.debug("size: {}×{} | components: {}", .{ img.width, img.height, img.num_components });
const grid_w = 4;
const grid_h = 4;
const tile_count = grid_w * grid_h;
const tile_w = std.math.divExact(u32, img.width, grid_w) catch |err| {
std.log.err("Cannot divide image width ({}) by {}: {s}", .{ img.width, grid_w, @errorName(err) });
return err;
};
const tile_h = std.math.divExact(u32, img.height, grid_h) catch |err| {
std.log.err("Cannot divide image height ({}) by {}: {s}", .{ img.height, grid_h, @errorName(err) });
return err;
};
std.log.debug("tile size: {}×{}", .{ tile_w, tile_h });
const data = allocator.alloc(u8, 4 * tile_w * tile_h * tile_count) catch |err| {
std.log.err("Ran out of memory while trying to allocate output buffer", .{});
return err;
};
errdefer allocator.free(data);
rearrange(grid_w, grid_h, tile_w, tile_h, img.data, data);
return .{
.width = tile_w,
.height = tile_h,
.depth = tile_count,
.data = data,
};
}
fn rearrange(grid_w: u32, grid_h: u32, tile_w: u32, tile_h: u32, inbuf: []const u8, outbuf: []u8) void {
std.log.debug("rearrange: {}×{} grid of {}×{} tiles", .{ grid_w, grid_h, tile_w, tile_h });
const row_size = 4 * tile_w;
const row_stride = row_size * grid_w;
const tile_stride = row_stride * tile_h;
std.debug.assert(inbuf.len == tile_stride * grid_h);
std.debug.assert(outbuf.len == tile_stride * grid_h);
var outptr: usize = 0;
var tile_y: u32 = 0;
while (tile_y < grid_h) : (tile_y += 1) {
const tile_byte_offset = tile_y * tile_stride;
var tile_x: u32 = 0;
while (tile_x < grid_w) : (tile_x += 1) {
const column_byte_offset = tile_x * row_size;
var row: u32 = 0;
while (row < tile_h) : (row += 1) {
const row_byte_offset = row * row_stride + tile_byte_offset;
const byte_offset = row_byte_offset + column_byte_offset;
@memcpy(
outbuf[outptr .. outptr + row_size],
inbuf[byte_offset .. byte_offset + row_size],
);
outptr += row_size;
}
}
}
}
pub fn deinitTextures(allocator: std.mem.Allocator, map: *AssetMap(Texture)) void {
var it = map.iterator();
while (it.next()) |entry| {
allocator.free(entry.key_ptr.*);
entry.value_ptr.deinit(allocator);
}
map.deinit(allocator);
}