Make use of some globals to stop typing engine everywhere.
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
const Blocks = @This();
|
||||
const std = @import("std");
|
||||
|
||||
const ctx = @import("../AppContext.zig");
|
||||
const media = @import("media");
|
||||
const voxels = @import("../voxels.zig");
|
||||
|
||||
@@ -76,23 +77,26 @@ pub const Definition = struct {
|
||||
}
|
||||
};
|
||||
|
||||
/// Maps a key value to a block definition ID.
|
||||
/// Maps a key value to a block definition ID. Preallocated with
|
||||
/// `allocator_general`.
|
||||
map: std.AutoHashMapUnmanaged(Key, Id),
|
||||
/// Stores all `Definition` structs and maps a block definition ID to a
|
||||
/// `Definition` struct.
|
||||
/// `Definition` struct. Preallocated with `allocator_general`.
|
||||
array: std.ArrayList(Definition),
|
||||
|
||||
/// With `@sizeOf(Definition) == 24` and `max_blocks = 4096`, the block
|
||||
/// definitions should take ~95.4 kiB in RAM.
|
||||
pub const max_blocks = 4096;
|
||||
|
||||
pub fn init(allocator: std.mem.Allocator) !Blocks {
|
||||
var map: std.AutoHashMapUnmanaged(Key, Id) = .empty;
|
||||
errdefer map.deinit(allocator);
|
||||
try map.ensureTotalCapacity(allocator, max_blocks);
|
||||
pub fn init() !Blocks {
|
||||
const allocator_general = ctx.allocator_general;
|
||||
|
||||
var array: std.ArrayList(Definition) = try .initCapacity(allocator, max_blocks);
|
||||
errdefer array.deinit(allocator);
|
||||
var map: std.AutoHashMapUnmanaged(Key, Id) = .empty;
|
||||
errdefer map.deinit(allocator_general);
|
||||
try map.ensureTotalCapacity(allocator_general, max_blocks);
|
||||
|
||||
var array: std.ArrayList(Definition) = try .initCapacity(allocator_general, max_blocks);
|
||||
errdefer array.deinit(allocator_general);
|
||||
|
||||
// VOLATILE Synchronize with explicit values on top of `Id` type.
|
||||
|
||||
@@ -104,11 +108,13 @@ pub fn init(allocator: std.mem.Allocator) !Blocks {
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Blocks, allocator: std.mem.Allocator) void {
|
||||
std.log.scoped(.deinit).debug("Deinitializing {*} with Allocator{{{*},{*}}}", .{ self, allocator.ptr, allocator.vtable });
|
||||
pub fn deinit(self: *Blocks) void {
|
||||
const allocator_general = ctx.allocator_general;
|
||||
|
||||
self.array.deinit(allocator);
|
||||
self.map.deinit(allocator);
|
||||
std.log.scoped(.deinit).debug("Deinitializing {*}", .{self});
|
||||
|
||||
self.array.deinit(allocator_general);
|
||||
self.map.deinit(allocator_general);
|
||||
self.* = undefined;
|
||||
}
|
||||
|
||||
@@ -132,18 +138,15 @@ pub fn getAtom(self: *const Blocks, filename: Atom) ?Id {
|
||||
|
||||
pub fn getOrLoad(
|
||||
self: *Blocks,
|
||||
engine: *Engine,
|
||||
materials: *Materials,
|
||||
textures: *Textures,
|
||||
stbi: *media.stbi,
|
||||
filename: []const u8,
|
||||
temp_allocator: std.mem.Allocator,
|
||||
io: std.Io,
|
||||
) !Id {
|
||||
const key: Key = .{
|
||||
// If the material already exists, then the atom must exist and the
|
||||
// following line will not return any error.
|
||||
.filename = try .fromString(filename, io),
|
||||
.filename = try .fromString(filename),
|
||||
};
|
||||
|
||||
// We don't use `getOrPutAssumeCapacity` method, because we might already be
|
||||
@@ -155,7 +158,7 @@ pub fn getOrLoad(
|
||||
const id = Id.fromIndexSafe(self.array.items.len) catch |err| switch (err) {
|
||||
error.Overflow => return error.OutOfBlocks,
|
||||
};
|
||||
const def = try loadBlock(engine, materials, textures, stbi, filename, temp_allocator, io);
|
||||
const def = try loadBlock(materials, textures, stbi, filename);
|
||||
|
||||
self.map.putAssumeCapacityNoClobber(key, id);
|
||||
self.array.appendAssumeCapacity(def);
|
||||
@@ -166,13 +169,10 @@ pub fn getOrLoad(
|
||||
|
||||
pub fn getOrLoadAtom(
|
||||
self: *Blocks,
|
||||
engine: *Engine,
|
||||
materials: *Materials,
|
||||
textures: *Textures,
|
||||
stbi: *media.stbi,
|
||||
filename: Atom,
|
||||
temp_allocator: std.mem.Allocator,
|
||||
io: std.Io,
|
||||
) !Id {
|
||||
const key: Key = .{
|
||||
.filename = filename,
|
||||
@@ -187,7 +187,7 @@ pub fn getOrLoadAtom(
|
||||
const id = Id.fromIndexSafe(self.array.items.len) catch |err| switch (err) {
|
||||
error.Overflow => return error.OutOfBlocks,
|
||||
};
|
||||
const def = try loadBlock(engine, materials, textures, stbi, filename.toString(), temp_allocator, io);
|
||||
const def = try loadBlock(materials, textures, stbi, filename.toString());
|
||||
|
||||
self.map.putAssumeCapacityNoClobber(key, id);
|
||||
self.array.appendAssumeCapacity(def);
|
||||
@@ -198,13 +198,12 @@ pub fn getOrLoadAtom(
|
||||
|
||||
pub fn loadAll(
|
||||
self: *Blocks,
|
||||
engine: *Engine,
|
||||
materials: *Materials,
|
||||
textures: *Textures,
|
||||
stbi: *media.stbi,
|
||||
temp_allocator: std.mem.Allocator,
|
||||
io: std.Io,
|
||||
) void {
|
||||
const io = ctx.io;
|
||||
|
||||
const cwd = std.Io.Dir.cwd();
|
||||
|
||||
var dir = cwd.openDir(io, "assets/blocks", .{ .iterate = true }) catch |err| {
|
||||
@@ -223,21 +222,21 @@ pub fn loadAll(
|
||||
continue;
|
||||
}
|
||||
|
||||
_ = self.getOrLoad(engine, materials, textures, stbi, entry.name, temp_allocator, io) catch |err| {
|
||||
_ = self.getOrLoad(materials, textures, stbi, entry.name) catch |err| {
|
||||
std.log.err("Error while loading block definition entry {s}: {s}", .{ entry.name, @errorName(err) });
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn loadBlock(
|
||||
engine: *Engine,
|
||||
materials: *Materials,
|
||||
textures: *Textures,
|
||||
stbi: *media.stbi,
|
||||
filename: []const u8,
|
||||
temp_allocator: std.mem.Allocator,
|
||||
io: std.Io,
|
||||
) !Definition {
|
||||
const allocator_frame = ctx.allocator_frame;
|
||||
const io = ctx.io;
|
||||
|
||||
const DefinitionJson = struct {
|
||||
pub const Wall = struct {
|
||||
material: ?[]const u8 = null,
|
||||
@@ -264,17 +263,13 @@ fn loadBlock(
|
||||
defer file.close(io);
|
||||
|
||||
var file_reader = file.reader(io, &buffer);
|
||||
var json_reader = std.json.Reader.init(temp_allocator, &file_reader.interface);
|
||||
defer json_reader.deinit();
|
||||
var json_reader = std.json.Reader.init(allocator_frame, &file_reader.interface);
|
||||
|
||||
const parsed: std.json.Parsed(DefinitionJson) = try std.json.parseFromTokenSource(DefinitionJson, temp_allocator, &json_reader, .{
|
||||
const def_json = try std.json.parseFromTokenSourceLeaky(DefinitionJson, allocator_frame, &json_reader, .{
|
||||
.duplicate_field_behavior = .@"error",
|
||||
.ignore_unknown_fields = false,
|
||||
.allocate = .alloc_if_needed,
|
||||
});
|
||||
defer parsed.deinit();
|
||||
|
||||
const def_json = parsed.value;
|
||||
|
||||
const block: Definition = blk: {
|
||||
if (def_json.material) |name| {
|
||||
@@ -283,7 +278,7 @@ fn loadBlock(
|
||||
return error.ParseError;
|
||||
}
|
||||
|
||||
const material = try materials.getOrLoad(engine, textures, stbi, name, temp_allocator, io);
|
||||
const material = try materials.getOrLoad(textures, stbi, name);
|
||||
break :blk .initUniform(material);
|
||||
}
|
||||
|
||||
@@ -293,7 +288,7 @@ fn loadBlock(
|
||||
var ret: Definition.Walls = undefined;
|
||||
inline for (@typeInfo(voxels.Orientation).@"enum".fields) |field| {
|
||||
@field(ret, field.name) = .{
|
||||
.material = try materials.getOrLoad(engine, textures, stbi, @field(walls, field.name).material, temp_allocator, io),
|
||||
.material = try materials.getOrLoad(textures, stbi, @field(walls, field.name).material),
|
||||
.transform = @field(walls, field.name).transform,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
const Chunk = @This();
|
||||
const std = @import("std");
|
||||
|
||||
const ctx = @import("../AppContext.zig");
|
||||
const math = @import("../math.zig");
|
||||
const shaders = @import("../shaders.zig");
|
||||
const vk = @import("vulkan");
|
||||
@@ -40,19 +41,21 @@ pub const InitInfo = struct {
|
||||
|
||||
pub const Neighbors = std.enums.EnumFieldStruct(voxels.Orientation, ?*const Chunk, null);
|
||||
|
||||
pub fn init(engine: *Engine, init_info: InitInfo) !Chunk {
|
||||
pub fn init(init_info: InitInfo) !Chunk {
|
||||
const engine = ctx.engine;
|
||||
|
||||
const descriptor_set = try engine.allocateDescriptorSet(.{
|
||||
.descriptor_pool = init_info.descriptor_pool,
|
||||
.set_layout = init_info.per_batch_descriptor_set_layout,
|
||||
});
|
||||
errdefer engine.freeDescriptorSet(init_info.descriptor_pool, descriptor_set);
|
||||
|
||||
var object_buffer: ObjectUniformsBuffer = try .init(engine, .{
|
||||
var object_buffer: ObjectUniformsBuffer = try .init(.{
|
||||
.usage = .storage,
|
||||
.target_queue = .graphics,
|
||||
.array_capacity = initial_capacity,
|
||||
});
|
||||
errdefer object_buffer.deinit(engine);
|
||||
errdefer object_buffer.deinit();
|
||||
|
||||
try engine.updateDescriptorSets(.{
|
||||
.writes = &.{
|
||||
@@ -89,18 +92,22 @@ pub fn init(engine: *Engine, init_info: InitInfo) !Chunk {
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Chunk, engine: *Engine, descriptor_pool: vk.DescriptorPool) void {
|
||||
std.log.scoped(.deinit).debug("Deinitializing chunk {d} with {*} and {s}#{X}", .{ self.chunk_id, engine, @typeName(vk.DescriptorPool), @intFromEnum(descriptor_pool) });
|
||||
pub fn deinit(self: *Chunk, descriptor_pool: vk.DescriptorPool) void {
|
||||
const engine = ctx.engine;
|
||||
|
||||
self.object_buffer.deinit(engine);
|
||||
std.log.scoped(.deinit).debug("Deinitializing chunk {d} with {s}#{X}", .{ self.chunk_id, @typeName(vk.DescriptorPool), @intFromEnum(descriptor_pool) });
|
||||
|
||||
self.object_buffer.deinit();
|
||||
engine.freeDescriptorSet(descriptor_pool, self.descriptor_set);
|
||||
|
||||
self.* = undefined;
|
||||
}
|
||||
|
||||
pub fn refresh(self: *Chunk, engine: *Engine, blocks: *const Blocks, neighbors: Neighbors, temp_allocator: std.mem.Allocator) !void {
|
||||
var uniforms: std.ArrayList(shaders.ObjectUniforms) = try .initCapacity(temp_allocator, initial_capacity);
|
||||
defer uniforms.deinit(temp_allocator);
|
||||
pub fn refresh(self: *Chunk, blocks: *const Blocks, neighbors: Neighbors) !void {
|
||||
const allocator_frame = ctx.allocator_frame;
|
||||
const engine = ctx.engine;
|
||||
|
||||
var uniforms: std.ArrayList(shaders.ObjectUniforms) = try .initCapacity(allocator_frame, initial_capacity);
|
||||
|
||||
for (self.blocks, 0..) |plane, iz| {
|
||||
const fz: f32 = @floatFromInt(iz);
|
||||
@@ -116,7 +123,7 @@ pub fn refresh(self: *Chunk, engine: *Engine, blocks: *const Blocks, neighbors:
|
||||
const material = @field(block.walls, field.name).material;
|
||||
if (material != .empty and self.getOpposite(side, ix, iy, iz, blocks, neighbors) == .empty) {
|
||||
const matrix = getMatrix(side, origin);
|
||||
try uniforms.append(temp_allocator, .{
|
||||
try uniforms.append(allocator_frame, .{
|
||||
.matrixOStoWS = matrix,
|
||||
.matrixOStoWSNormal = matrix,
|
||||
.material = material,
|
||||
@@ -130,7 +137,7 @@ pub fn refresh(self: *Chunk, engine: *Engine, blocks: *const Blocks, neighbors:
|
||||
const object_count: u32 = @intCast(uniforms.items.len);
|
||||
if (self.object_buffer.array_capacity < object_count) {
|
||||
const desired_capacity = std.math.ceilPowerOfTwoAssert(u32, object_count);
|
||||
const new_object_buffer: ObjectUniformsBuffer = try .init(engine, .{
|
||||
const new_object_buffer: ObjectUniformsBuffer = try .init(.{
|
||||
.usage = .storage,
|
||||
.target_queue = .graphics,
|
||||
.array_capacity = desired_capacity,
|
||||
@@ -156,13 +163,13 @@ pub fn refresh(self: *Chunk, engine: *Engine, blocks: *const Blocks, neighbors:
|
||||
},
|
||||
});
|
||||
|
||||
self.object_buffer.deinit(engine);
|
||||
self.object_buffer.deinit();
|
||||
self.object_buffer = new_object_buffer;
|
||||
engine.setObjectName(new_object_buffer.buffer, "B Chunk[{d}]", .{self.chunk_id});
|
||||
engine.setObjectName(new_object_buffer.device_memory, "DM Chunk[{d}]", .{self.chunk_id});
|
||||
}
|
||||
|
||||
try self.object_buffer.write(engine, .{ .elements = uniforms.items });
|
||||
try self.object_buffer.write(.{ .elements = uniforms.items });
|
||||
self.object_count = object_count;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
const Materials = @This();
|
||||
const std = @import("std");
|
||||
|
||||
const ctx = @import("../AppContext.zig");
|
||||
const media = @import("media");
|
||||
const shaders = @import("../shaders.zig");
|
||||
const vk = @import("vulkan");
|
||||
@@ -50,7 +51,7 @@ pub const Key = struct {
|
||||
filename: Atom,
|
||||
};
|
||||
|
||||
/// Maps a key value to a material ID.
|
||||
/// Maps a key value to a material ID. Preallocated with `allocator_general`.
|
||||
map: std.AutoHashMapUnmanaged(Key, Id),
|
||||
/// Stores all material data in a single contiguous storage buffer. Use the
|
||||
/// material ID as an index into this buffer.
|
||||
@@ -62,22 +63,24 @@ material_count: usize,
|
||||
/// storage buffer should take 208 kiB in VRAM.
|
||||
pub const max_materials = 4096;
|
||||
|
||||
pub fn init(engine: *Engine, allocator: std.mem.Allocator) !Materials {
|
||||
var map: std.AutoHashMapUnmanaged(Key, Id) = .empty;
|
||||
errdefer map.deinit(allocator);
|
||||
try map.ensureTotalCapacity(allocator, max_materials);
|
||||
pub fn init() !Materials {
|
||||
const allocator_general = ctx.allocator_general;
|
||||
|
||||
var material_buffer = try shaders.MaterialBuffer.init(engine, .{
|
||||
var map: std.AutoHashMapUnmanaged(Key, Id) = .empty;
|
||||
errdefer map.deinit(allocator_general);
|
||||
try map.ensureTotalCapacity(allocator_general, max_materials);
|
||||
|
||||
var material_buffer = try shaders.MaterialBuffer.init(.{
|
||||
.usage = .storage,
|
||||
.target_queue = .graphics,
|
||||
.array_capacity = max_materials,
|
||||
.name = "Materials",
|
||||
});
|
||||
errdefer material_buffer.deinit(engine);
|
||||
errdefer material_buffer.deinit();
|
||||
|
||||
// VOLATILE Synchronize with explicit values on top of `Id` type.
|
||||
|
||||
try material_buffer.write(engine, .{
|
||||
try material_buffer.write(.{
|
||||
.element_offset = Id.empty.toInt(),
|
||||
.elements = &.{
|
||||
.{
|
||||
@@ -103,11 +106,13 @@ pub fn init(engine: *Engine, allocator: std.mem.Allocator) !Materials {
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Materials, engine: *Engine, allocator: std.mem.Allocator) void {
|
||||
std.log.scoped(.deinit).debug("Deinitializing {*} with {*} and Allocator{{{*},{*}}}", .{ self, engine, allocator.ptr, allocator.vtable });
|
||||
pub fn deinit(self: *Materials) void {
|
||||
const allocator_general = ctx.allocator_general;
|
||||
|
||||
self.material_buffer.deinit(engine);
|
||||
self.map.deinit(allocator);
|
||||
std.log.scoped(.deinit).debug("Deinitializing {*}", .{self});
|
||||
|
||||
self.material_buffer.deinit();
|
||||
self.map.deinit(allocator_general);
|
||||
self.* = undefined;
|
||||
}
|
||||
|
||||
@@ -151,18 +156,15 @@ pub fn getAtom(self: *const Materials, filename: Atom) ?Id {
|
||||
/// error is not necessarily related to `temp_allocator`.
|
||||
pub fn getOrLoad(
|
||||
self: *Materials,
|
||||
engine: *Engine,
|
||||
textures: *Textures,
|
||||
stbi: *media.stbi,
|
||||
maybe_filename: ?[]const u8,
|
||||
temp_allocator: std.mem.Allocator,
|
||||
io: std.Io,
|
||||
) !Id {
|
||||
if (maybe_filename) |filename| {
|
||||
const key: Key = .{
|
||||
// If the material already exists, then the atom must exist and the
|
||||
// following line will not return any error.
|
||||
.filename = try .fromString(filename, io),
|
||||
.filename = try .fromString(filename),
|
||||
};
|
||||
|
||||
// We don't use `getOrPutAssumeCapacity` method, because we might already be
|
||||
@@ -174,7 +176,7 @@ pub fn getOrLoad(
|
||||
const id = Id.fromIndexSafe(self.material_count) catch |err| switch (err) {
|
||||
error.Overflow => return error.OutOfMaterials,
|
||||
};
|
||||
try self.loadMaterial(engine, textures, stbi, filename, id.toInt(), temp_allocator, io);
|
||||
try self.loadMaterial(textures, stbi, filename, id.toInt());
|
||||
|
||||
self.map.putAssumeCapacityNoClobber(key, id);
|
||||
self.material_count += 1;
|
||||
@@ -199,12 +201,9 @@ pub fn getOrLoad(
|
||||
/// error is not necessarily related to `temp_allocator`.
|
||||
pub fn getOrLoadAtom(
|
||||
self: *Materials,
|
||||
engine: *Engine,
|
||||
textures: *Textures,
|
||||
stbi: *media.stbi,
|
||||
filename: Atom,
|
||||
temp_allocator: std.mem.Allocator,
|
||||
io: std.Io,
|
||||
) !Id {
|
||||
if (filename != .empty) {
|
||||
const key: Key = .{
|
||||
@@ -220,7 +219,7 @@ pub fn getOrLoadAtom(
|
||||
const id = Id.fromIndexSafe(self.material_count) catch |err| switch (err) {
|
||||
error.Overflow => return error.OutOfMaterials,
|
||||
};
|
||||
try self.loadMaterial(engine, textures, stbi, filename.toString(), id.toInt(), temp_allocator, io);
|
||||
try self.loadMaterial(textures, stbi, filename.toString(), id.toInt());
|
||||
|
||||
self.map.putAssumeCapacityNoClobber(key, id);
|
||||
self.material_count += 1;
|
||||
@@ -242,14 +241,20 @@ pub fn getOrLoadAtom(
|
||||
/// deinitialized or reset after this function returns. Note that during loading
|
||||
/// the engine will make its own persistent allocations, so an out of memory
|
||||
/// error is not necessarily related to `temp_allocator`.
|
||||
pub fn loadAll(self: *Materials, engine: *Engine, textures: *Textures, stbi: *media.stbi, temp_allocator: std.mem.Allocator) void {
|
||||
const cwd = std.fs.cwd();
|
||||
pub fn loadAll(
|
||||
self: *Materials,
|
||||
textures: *Textures,
|
||||
stbi: *media.stbi,
|
||||
) void {
|
||||
const io = ctx.io;
|
||||
|
||||
var dir = cwd.openDir("assets/materials", .{ .iterate = true }) catch |err| {
|
||||
const cwd = std.Io.Dir.cwd();
|
||||
|
||||
var dir = cwd.openDir(io, "assets/materials", .{ .iterate = true }) catch |err| {
|
||||
std.log.err("Error while opening metarials directory: {s}", .{@errorName(err)});
|
||||
return;
|
||||
};
|
||||
defer dir.close();
|
||||
defer dir.close(io);
|
||||
|
||||
var it = dir.iterate();
|
||||
while (it.next() catch |err| {
|
||||
@@ -261,13 +266,16 @@ pub fn loadAll(self: *Materials, engine: *Engine, textures: *Textures, stbi: *me
|
||||
continue;
|
||||
}
|
||||
|
||||
_ = self.getOrLoad(engine, textures, stbi, entry.name, temp_allocator) catch |err| {
|
||||
_ = self.getOrLoad(textures, stbi, entry.name) catch |err| {
|
||||
std.log.err("Error while loading material entry {s}: {s}", .{ entry.name, @errorName(err) });
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn loadMaterial(self: *Materials, engine: *Engine, textures: *Textures, stbi: *media.stbi, filename: []const u8, index: u32, temp_allocator: std.mem.Allocator, io: std.Io) !void {
|
||||
fn loadMaterial(self: *Materials, textures: *Textures, stbi: *media.stbi, filename: []const u8, index: u32) !void {
|
||||
const allocator_frame = ctx.allocator_frame;
|
||||
const io = ctx.io;
|
||||
|
||||
const MaterialJson = struct {
|
||||
baseColor: [3]f32 = .{ 1, 1, 1 },
|
||||
baseColorTexture: ?[]const u8 = null,
|
||||
@@ -296,19 +304,15 @@ fn loadMaterial(self: *Materials, engine: *Engine, textures: *Textures, stbi: *m
|
||||
defer file.close(io);
|
||||
|
||||
var file_reader = file.reader(io, &buffer);
|
||||
var json_reader = std.json.Reader.init(temp_allocator, &file_reader.interface);
|
||||
defer json_reader.deinit();
|
||||
var json_reader = std.json.Reader.init(allocator_frame, &file_reader.interface);
|
||||
|
||||
const parsed: std.json.Parsed(MaterialJson) = try std.json.parseFromTokenSource(MaterialJson, temp_allocator, &json_reader, .{
|
||||
const material_json = try std.json.parseFromTokenSourceLeaky(MaterialJson, allocator_frame, &json_reader, .{
|
||||
.duplicate_field_behavior = .@"error",
|
||||
.ignore_unknown_fields = false,
|
||||
.allocate = .alloc_if_needed,
|
||||
});
|
||||
defer parsed.deinit();
|
||||
|
||||
const material_json = parsed.value;
|
||||
|
||||
try self.material_buffer.write(engine, .{
|
||||
try self.material_buffer.write(.{
|
||||
.element_offset = index,
|
||||
.elements = &.{
|
||||
.{
|
||||
@@ -319,10 +323,10 @@ fn loadMaterial(self: *Materials, engine: *Engine, textures: *Textures, stbi: *m
|
||||
.normal_scale = material_json.normalScale,
|
||||
.occlusion_texture_strength = material_json.occlusionTextureStrength,
|
||||
.roughness = material_json.roughness,
|
||||
.base_color_texture = try textures.getOrLoad(engine, stbi, material_json.baseColorTexture, .base_color, io),
|
||||
.emissive_texture = try textures.getOrLoad(engine, stbi, material_json.emissiveTexture, .emissive, io),
|
||||
.normal_texture = try textures.getOrLoad(engine, stbi, material_json.normalTexture, .normal, io),
|
||||
.occlusion_roughness_metallic_texture = try textures.getOrLoad(engine, stbi, material_json.occlusionRoughnessMetallicTexture, .occlusion_roughness_metallic, io),
|
||||
.base_color_texture = try textures.getOrLoad(stbi, material_json.baseColorTexture, .base_color),
|
||||
.emissive_texture = try textures.getOrLoad(stbi, material_json.emissiveTexture, .emissive),
|
||||
.normal_texture = try textures.getOrLoad(stbi, material_json.normalTexture, .normal),
|
||||
.occlusion_roughness_metallic_texture = try textures.getOrLoad(stbi, material_json.occlusionRoughnessMetallicTexture, .occlusion_roughness_metallic),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
const Textures = @This();
|
||||
const std = @import("std");
|
||||
|
||||
const ctx = @import("../AppContext.zig");
|
||||
const media = @import("media");
|
||||
|
||||
const Atom = @import("../engine/Atom.zig").Atom;
|
||||
@@ -56,31 +57,34 @@ pub const Key = struct {
|
||||
usage: Texture.Usage,
|
||||
};
|
||||
|
||||
/// Maps a key value to a texture ID.
|
||||
/// Maps a key value to a texture ID. Preallocated with `allocator_general`.
|
||||
map: std.AutoHashMapUnmanaged(Key, Id),
|
||||
/// Stores all `Texture` structs and maps a texture ID to a `Texture` struct.
|
||||
/// Preallocated with `allocator_general`.
|
||||
array: std.ArrayList(Texture),
|
||||
|
||||
/// 4096 textures of usage `.base_color` and 16×16 dimensions should take 4 MiB
|
||||
/// in VRAM.
|
||||
pub const max_textures = 4096;
|
||||
|
||||
pub fn init(engine: *Engine, allocator: std.mem.Allocator) !Textures {
|
||||
var map: std.AutoHashMapUnmanaged(Key, Id) = .empty;
|
||||
errdefer map.deinit(allocator);
|
||||
try map.ensureTotalCapacity(allocator, max_textures);
|
||||
pub fn init() !Textures {
|
||||
const allocator_general = ctx.allocator_general;
|
||||
|
||||
var array: std.ArrayList(Texture) = try .initCapacity(allocator, max_textures);
|
||||
var map: std.AutoHashMapUnmanaged(Key, Id) = .empty;
|
||||
errdefer map.deinit(allocator_general);
|
||||
try map.ensureTotalCapacity(allocator_general, max_textures);
|
||||
|
||||
var array: std.ArrayList(Texture) = try .initCapacity(allocator_general, max_textures);
|
||||
errdefer {
|
||||
for (array.items) |*texture| {
|
||||
texture.deinit(engine);
|
||||
texture.deinit();
|
||||
}
|
||||
array.deinit(allocator);
|
||||
array.deinit(allocator_general);
|
||||
}
|
||||
|
||||
// VOLATILE Synchronize with explicit values on top of `Id` type.
|
||||
|
||||
const empty_base_color_texture = try Texture.init(engine, .{
|
||||
const empty_base_color_texture = try Texture.init(.{
|
||||
.width = 1,
|
||||
.height = 1,
|
||||
.usage = .base_color,
|
||||
@@ -89,7 +93,7 @@ pub fn init(engine: *Engine, allocator: std.mem.Allocator) !Textures {
|
||||
});
|
||||
array.appendAssumeCapacity(empty_base_color_texture);
|
||||
|
||||
const empty_emissive_texture = try Texture.init(engine, .{
|
||||
const empty_emissive_texture = try Texture.init(.{
|
||||
.width = 1,
|
||||
.height = 1,
|
||||
.usage = .emissive,
|
||||
@@ -98,7 +102,7 @@ pub fn init(engine: *Engine, allocator: std.mem.Allocator) !Textures {
|
||||
});
|
||||
array.appendAssumeCapacity(empty_emissive_texture);
|
||||
|
||||
const empty_normal_texture = try Texture.init(engine, .{
|
||||
const empty_normal_texture = try Texture.init(.{
|
||||
.width = 1,
|
||||
.height = 1,
|
||||
.usage = .normal,
|
||||
@@ -107,7 +111,7 @@ pub fn init(engine: *Engine, allocator: std.mem.Allocator) !Textures {
|
||||
});
|
||||
array.appendAssumeCapacity(empty_normal_texture);
|
||||
|
||||
const empty_occlusuion_roughness_metallic_texture = try Texture.init(engine, .{
|
||||
const empty_occlusuion_roughness_metallic_texture = try Texture.init(.{
|
||||
.width = 1,
|
||||
.height = 1,
|
||||
.usage = .occlusion_roughness_metallic,
|
||||
@@ -116,10 +120,10 @@ pub fn init(engine: *Engine, allocator: std.mem.Allocator) !Textures {
|
||||
});
|
||||
array.appendAssumeCapacity(empty_occlusuion_roughness_metallic_texture);
|
||||
|
||||
try empty_base_color_texture.writeSamples(u8, engine, &.{ 255, 255, 255, 255 });
|
||||
try empty_emissive_texture.writeSamples(f16, engine, &.{ 1.0, 1.0, 1.0, 1.0 });
|
||||
try empty_normal_texture.writeSamples(i8, engine, &.{ 0, 0, 127, 127 });
|
||||
try empty_occlusuion_roughness_metallic_texture.writeSamples(u8, engine, &.{ 255, 255, 255, 255 });
|
||||
try empty_base_color_texture.writeSamples(u8, &.{ 255, 255, 255, 255 });
|
||||
try empty_emissive_texture.writeSamples(f16, &.{ 1.0, 1.0, 1.0, 1.0 });
|
||||
try empty_normal_texture.writeSamples(i8, &.{ 0, 0, 127, 127 });
|
||||
try empty_occlusuion_roughness_metallic_texture.writeSamples(u8, &.{ 255, 255, 255, 255 });
|
||||
|
||||
return .{
|
||||
.map = map,
|
||||
@@ -127,14 +131,16 @@ pub fn init(engine: *Engine, allocator: std.mem.Allocator) !Textures {
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Textures, engine: *Engine, allocator: std.mem.Allocator) void {
|
||||
std.log.scoped(.deinit).debug("Deinitializing {*} with {*} and Allocator{{{*},{*}}}", .{ self, engine, allocator.ptr, allocator.vtable });
|
||||
pub fn deinit(self: *Textures) void {
|
||||
const allocator_general = ctx.allocator_general;
|
||||
|
||||
std.log.scoped(.deinit).debug("Deinitializing {*}", .{self});
|
||||
|
||||
for (self.array.items) |*texture| {
|
||||
texture.deinit(engine);
|
||||
texture.deinit();
|
||||
}
|
||||
self.array.deinit(allocator);
|
||||
self.map.deinit(allocator);
|
||||
self.array.deinit(allocator_general);
|
||||
self.map.deinit(allocator_general);
|
||||
self.* = undefined;
|
||||
}
|
||||
|
||||
@@ -174,17 +180,15 @@ pub fn getAtom(self: *const Textures, filename: Atom, usage: Texture.Usage) ?Id
|
||||
/// usage.
|
||||
pub fn getOrLoad(
|
||||
self: *Textures,
|
||||
engine: *Engine,
|
||||
stbi: *media.stbi,
|
||||
maybe_filename: ?[]const u8,
|
||||
usage: Texture.Usage,
|
||||
io: std.Io,
|
||||
) !Id {
|
||||
if (maybe_filename) |filename| {
|
||||
const key: Key = .{
|
||||
// If the texture already exists, then the atom must exist and the
|
||||
// following line will not return any error.
|
||||
.filename = try .fromString(filename, io),
|
||||
.filename = try .fromString(filename),
|
||||
.usage = usage,
|
||||
};
|
||||
|
||||
@@ -197,7 +201,7 @@ pub fn getOrLoad(
|
||||
const id = Id.fromIndexSafe(self.array.items.len) catch |err| switch (err) {
|
||||
error.Overflow => return error.OutOfTextures,
|
||||
};
|
||||
const texture = try loadTexture(engine, stbi, filename, usage, io);
|
||||
const texture = try loadTexture(stbi, filename, usage);
|
||||
|
||||
self.map.putAssumeCapacityNoClobber(key, id);
|
||||
self.array.appendAssumeCapacity(texture);
|
||||
@@ -216,11 +220,9 @@ pub fn getOrLoad(
|
||||
/// usage.
|
||||
pub fn getOrLoadAtom(
|
||||
self: *Textures,
|
||||
engine: *Engine,
|
||||
stbi: *media.stbi,
|
||||
filename: Atom,
|
||||
usage: Texture.Usage,
|
||||
io: std.Io,
|
||||
) !Id {
|
||||
if (filename != .empty) {
|
||||
const key: Key = .{
|
||||
@@ -237,7 +239,7 @@ pub fn getOrLoadAtom(
|
||||
const id = Id.fromIndexSafe(self.array.items.len) catch |err| switch (err) {
|
||||
error.Overflow => return error.OutOfTextures,
|
||||
};
|
||||
const texture = try loadTexture(engine, stbi, filename.toString(), usage, io);
|
||||
const texture = try loadTexture(stbi, filename.toString(), usage);
|
||||
|
||||
self.map.putAssumeCapacityNoClobber(key, id);
|
||||
self.array.appendAssumeCapacity(texture);
|
||||
@@ -250,12 +252,12 @@ pub fn getOrLoadAtom(
|
||||
}
|
||||
|
||||
fn loadTexture(
|
||||
engine: *Engine,
|
||||
stbi: *media.stbi,
|
||||
filename: []const u8,
|
||||
usage: Texture.Usage,
|
||||
io: std.Io,
|
||||
) !Texture {
|
||||
const io = ctx.io;
|
||||
|
||||
std.log.debug("Loading texture \"{s}\" as {s}...", .{ filename, @tagName(usage) });
|
||||
|
||||
const cwd = std.Io.Dir.cwd();
|
||||
@@ -285,16 +287,16 @@ fn loadTexture(
|
||||
}
|
||||
}
|
||||
|
||||
var texture = try Texture.init(engine, .{
|
||||
var texture = try Texture.init(.{
|
||||
.width = img.width,
|
||||
.height = img.height,
|
||||
.usage = usage,
|
||||
.target_queue = .graphics,
|
||||
.name = filename,
|
||||
});
|
||||
errdefer texture.deinit(engine);
|
||||
errdefer texture.deinit();
|
||||
|
||||
try texture.writeRaw(engine, @ptrCast(data));
|
||||
try texture.writeRaw(@ptrCast(data));
|
||||
return texture;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user