144 lines
4.4 KiB
Zig
144 lines
4.4 KiB
Zig
const Textures = @This();
|
|
const std = @import("std");
|
|
|
|
const stbi = @import("zstbi");
|
|
|
|
const Atoms = @import("Atoms.zig");
|
|
const Engine = @import("../engine/Engine.zig");
|
|
const Texture = @import("../engine/Texture.zig");
|
|
|
|
allocator: std.mem.Allocator,
|
|
map: Map,
|
|
array: Array,
|
|
|
|
empty_base_color: Id,
|
|
empty_emissive: Id,
|
|
empty_normal: Id,
|
|
empty_occlusuion_roughness_metallic: Id,
|
|
|
|
pub const Id = extern struct {
|
|
id: u32,
|
|
};
|
|
|
|
pub const Key = struct {
|
|
atom: Atoms.Atom,
|
|
usage: Texture.Usage,
|
|
};
|
|
|
|
pub const Map = std.AutoHashMapUnmanaged(Key, Id);
|
|
pub const Array = std.ArrayList(Texture);
|
|
|
|
pub fn init(allocator: std.mem.Allocator, engine: *Engine) !Textures {
|
|
var map: Map = .empty;
|
|
errdefer map.deinit(allocator);
|
|
|
|
var array: Array = try .initCapacity(allocator, 4);
|
|
errdefer {
|
|
for (array.items) |*texture| {
|
|
texture.deinit(engine);
|
|
}
|
|
array.deinit(allocator);
|
|
}
|
|
|
|
const empty_base_color: Id = .{ .id = @intCast(array.items.len) };
|
|
const empty_base_color_texture: Texture = try .init(engine, 1, 1, .base_color);
|
|
array.appendAssumeCapacity(empty_base_color_texture);
|
|
|
|
const empty_emissive: Id = .{ .id = @intCast(array.items.len) };
|
|
const empty_emissive_texture: Texture = try .init(engine, 1, 1, .emissive);
|
|
array.appendAssumeCapacity(empty_emissive_texture);
|
|
|
|
const empty_normal: Id = .{ .id = @intCast(array.items.len) };
|
|
const empty_normal_texture: Texture = try .init(engine, 1, 1, .normal);
|
|
array.appendAssumeCapacity(empty_normal_texture);
|
|
|
|
const empty_occlusuion_roughness_metallic: Id = .{ .id = @intCast(array.items.len) };
|
|
const empty_occlusuion_roughness_metallic_texture: Texture = try .init(engine, 1, 1, .occlusion_roughness_metallic);
|
|
array.appendAssumeCapacity(empty_occlusuion_roughness_metallic_texture);
|
|
|
|
try empty_base_color_texture.write([4]u8, engine, &.{.{ 255, 255, 255, 255 }});
|
|
try empty_emissive_texture.write([4]f16, engine, &.{.{ 1.0, 1.0, 1.0, 1.0 }});
|
|
try empty_normal_texture.write([4]i8, engine, &.{.{ 0, 0, 127, 127 }});
|
|
try empty_occlusuion_roughness_metallic_texture.write([4]u8, engine, &.{.{ 255, 255, 255, 255 }});
|
|
|
|
return .{
|
|
.allocator = allocator,
|
|
.map = map,
|
|
.array = array,
|
|
|
|
.empty_base_color = empty_base_color,
|
|
.empty_emissive = empty_emissive,
|
|
.empty_normal = empty_normal,
|
|
.empty_occlusuion_roughness_metallic = empty_occlusuion_roughness_metallic,
|
|
};
|
|
}
|
|
|
|
pub fn deinit(self: *Textures, engine: *Engine) void {
|
|
for (self.array.items) |*texture| {
|
|
texture.deinit(engine);
|
|
}
|
|
self.array.deinit(self.allocator);
|
|
|
|
self.map.deinit(self.allocator);
|
|
}
|
|
|
|
pub fn getTexture(self: *const Textures, id: Id) ?*Texture {
|
|
const index: usize = id.id;
|
|
return if (index < self.array.items.len) &self.array.items[index] else null;
|
|
}
|
|
|
|
pub fn getId(self: *const Textures, key: Key) ?Id {
|
|
return self.map.get(key);
|
|
}
|
|
|
|
pub fn getOrLoadId(self: *Textures, engine: *Engine, atoms: *Atoms, key: Key) !Id {
|
|
const entry = try self.map.getOrPut(self.allocator, key);
|
|
|
|
if (entry.found_existing) {
|
|
return entry.value_ptr.*;
|
|
} else {
|
|
errdefer _ = self.map.remove(key);
|
|
try self.array.ensureUnusedCapacity(self.allocator, 1);
|
|
const texture = try self.loadTexture(engine, atoms, key);
|
|
|
|
const id = self.nextId();
|
|
|
|
entry.value_ptr.* = id;
|
|
self.array.appendAssumeCapacity(texture);
|
|
|
|
return id;
|
|
}
|
|
}
|
|
|
|
fn loadTexture(self: *Textures, engine: *Engine, atoms: *Atoms, key: Key) !Texture {
|
|
const filename = atoms.getString(key.atom).?;
|
|
std.log.debug("Loading texture \"{s}\" as {s}...", .{ filename, @tagName(key.usage) });
|
|
|
|
const cwd = std.fs.cwd();
|
|
|
|
var dir = try cwd.openDir("assets/textures", .{});
|
|
defer dir.close();
|
|
|
|
const file = try dir.openFile(filename, .{});
|
|
defer file.close();
|
|
|
|
const file_buf = try file.readToEndAlloc(self.allocator, std.math.maxInt(usize));
|
|
defer self.allocator.free(file_buf);
|
|
|
|
var img = try stbi.Image.loadFromMemory(file_buf, key.usage.sampleCount());
|
|
defer img.deinit();
|
|
std.debug.assert(img.num_components == key.usage.sampleCount());
|
|
|
|
var texture: Texture = try .init(engine, img.width, img.height, key.usage);
|
|
errdefer texture.deinit(engine);
|
|
|
|
try texture.write(u8, engine, img.data);
|
|
|
|
return texture;
|
|
}
|
|
|
|
fn nextId(self: *const Textures) Id {
|
|
const index = self.array.items.len;
|
|
return .{ .id = @intCast(index) };
|
|
}
|