It works!

This commit is contained in:
2025-11-26 15:47:02 +01:00
parent 9f2d1e4608
commit b9a804ead6
8 changed files with 150 additions and 88 deletions

View File

@@ -104,7 +104,7 @@ swapchain: *Swapchain,
global_descriptor_set_layout: vk.DescriptorSetLayout,
per_batch_descriptor_set_layout: vk.DescriptorSetLayout,
descriptor_pool: vk.DescriptorPool,
/// [0] GLOBAL, [1] PER OBJECT
/// [0] GLOBAL, [1] PER BATCH
descriptor_sets: [2]vk.DescriptorSet,
pipeline_layout: vk.PipelineLayout,
pipeline: vk.Pipeline,
@@ -117,6 +117,7 @@ point_lights: PointLightBuffer,
directional_lights: DirectionalLightBuffer,
object_uniforms: ObjectUniformsBuffer,
sampler: vk.Sampler,
object_count: u32,
materials: Materials,
textures: Textures,
@@ -254,25 +255,25 @@ pub fn init(allocator: std.mem.Allocator, engine: *Engine, swapchain: *Swapchain
.init(
.init(-0.5, -0.5, 0),
.init(0, 1),
.init(1, 0, 0),
.init(0, 0, 1),
.init(1, 0, 0, -1),
),
.init(
.init(0.5, -0.5, 0),
.init(1, 1),
.init(1, 0, 0),
.init(0, 0, 1),
.init(1, 0, 0, -1),
),
.init(
.init(-0.5, 0.5, 0),
.init(0, 0),
.init(1, 0, 0),
.init(0, 0, 1),
.init(1, 0, 0, -1),
),
.init(
.init(0.5, 0.5, 0),
.init(1, 0),
.init(1, 0, 0),
.init(0, 0, 1),
.init(1, 0, 0, -1),
),
},
@@ -549,6 +550,26 @@ pub fn init(allocator: std.mem.Allocator, engine: *Engine, swapchain: *Swapchain
},
});
const object_count: u32 = blk: {
var objects: std.ArrayList(ObjectUniforms) = try .initCapacity(allocator, 289);
defer objects.deinit(allocator);
var it = Iterator3.init(.init(-8, -8, 0), .init(8, 8, 0), .one);
while (it.next()) |pos| {
const material: Materials.Id = @enumFromInt(engine.random.uintLessThan(u16, @intFromEnum(materials.next_id)));
const matrix_os_to_ws = Matrix4x4.initTranslation(pos);
const matrix_os_to_ws_normal = Matrix4x4.inverseTransposeAffine(matrix_os_to_ws);
objects.appendAssumeCapacity(.{
.matrixOStoWS = matrix_os_to_ws.asArray(),
.matrixOStoWSNormal = matrix_os_to_ws_normal.asArray(),
.material = material,
});
}
try object_uniforms.write(engine, .{ .elements = objects.items });
break :blk @intCast(objects.items.len);
};
return .{
.allocator = allocator,
.engine = engine,
@@ -568,6 +589,7 @@ pub fn init(allocator: std.mem.Allocator, engine: *Engine, swapchain: *Swapchain
.directional_lights = directional_lights,
.object_uniforms = object_uniforms,
.sampler = sampler,
.object_count = object_count,
.materials = materials,
.textures = textures,
@@ -623,14 +645,14 @@ pub fn update(self: *Game, dt: f32) void {
// zig fmt: off
const matrix_vs_to_cs = Matrix4x4.init(
camera_xscale, 0, 0, 0,
0, 0, camera_near_plane, 1,
0, camera_yscale, 0, 0,
0, 0, 0, 0,
camera_xscale, 0, 0, 0,
0, 0, camera_near_plane, 1,
0, -camera_yscale, 0, 0,
0, 0, 0, 0,
);
// zig fmt: on
const ambient_light = Vector3.init(0.3, 0.3, 0.3);
const ambient_light = Vector3.init(0.1, 0.1, 0.1);
self.global_uniforms.write(self.engine, .{
.header = .{
@@ -649,19 +671,19 @@ pub fn update(self: *Game, dt: f32) void {
.color = .{ 10, 10, 10 },
},
.{
.positionWS = .{ -10, 10, 1 },
.positionWS = .{ -7, 7, 1 },
.color = .{ 5, 0, 0 },
},
.{
.positionWS = .{ 10, 10, 1 },
.positionWS = .{ 7, 7, 1 },
.color = .{ 0, 0, 5 },
},
.{
.positionWS = .{ -10, -10, 1 },
.positionWS = .{ -7, -7, 1 },
.color = .{ 0, 5, 0 },
},
.{
.positionWS = .{ 10, -10, 1 },
.positionWS = .{ 7, -7, 1 },
.color = .{ 5, 5, 0 },
},
};
@@ -747,51 +769,10 @@ fn render(self: *Game) !void {
const engine = self.engine;
const extent = self.swapchain.extent;
const object_count: u32 = blk: {
var objects: std.ArrayList(ObjectUniforms) = try .initCapacity(self.engine.vk_allocator.allocator, 289);
defer objects.deinit(self.engine.vk_allocator.allocator);
var it = Iterator3.init(.init(-8, -8, 0), .init(8, 8, 0), .one);
var material: u16 = 0;
while (it.next()) |pos| : (material = (material + 1) % @intFromEnum(self.materials.next_id)) {
const matrix_os_to_ws = Matrix4x4.initTranslation(pos);
const matrix_os_to_ws_normal = Matrix4x4.inverseTransposeAffine(matrix_os_to_ws);
objects.appendAssumeCapacity(.{
.matrixOStoWS = matrix_os_to_ws.asArray(),
.matrixOStoWSNormal = matrix_os_to_ws_normal.asArray(),
.material = @enumFromInt(material),
});
}
try self.object_uniforms.write(self.engine, .{ .elements = objects.items });
break :blk @intCast(objects.items.len);
};
const command_buffer = try engine.allocateGraphicsCommandBuffer();
// NOTE Do not free command buffer yet
try command_buffer.beginCommandBuffer(&.{ .flags = .{ .one_time_submit_bit = true } });
const viewports = [_]vk.Viewport{
.{
.x = 0,
.y = 0,
.width = @floatFromInt(extent.width),
.height = @floatFromInt(extent.height),
.min_depth = 0,
.max_depth = 1,
},
};
command_buffer.setViewport(0, viewports.len, &viewports);
const scissors = [_]vk.Rect2D{
.{
.offset = .{ .x = 0, .y = 0 },
.extent = extent,
},
};
command_buffer.setScissor(0, scissors.len, &scissors);
{
const clear_values = [_]vk.ClearValue{
.{ .color = .{ .float_32 = .{ 0, 0, 0, 1 } } },
@@ -799,7 +780,7 @@ fn render(self: *Game) !void {
command_buffer.beginRenderPass(&.{
.render_pass = self.swapchain.render_pass,
.framebuffer = self.swapchain.swapchain_images[0].framebuffer,
.framebuffer = self.swapchain.swapchain_images[self.swapchain.image_index].framebuffer,
.render_area = .{
.offset = .{ .x = 0, .y = 0 },
.extent = extent,
@@ -809,25 +790,36 @@ fn render(self: *Game) !void {
}, .@"inline");
defer command_buffer.endRenderPass();
const viewports = [_]vk.Viewport{
.{
.x = 0,
.y = 0,
.width = @floatFromInt(extent.width),
.height = @floatFromInt(extent.height),
.min_depth = 0,
.max_depth = 1,
},
};
command_buffer.setViewport(0, viewports.len, &viewports);
const scissors = [_]vk.Rect2D{
.{
.offset = .{ .x = 0, .y = 0 },
.extent = extent,
},
};
command_buffer.setScissor(0, scissors.len, &scissors);
command_buffer.bindPipeline(.graphics, self.pipeline);
command_buffer.bindVertexBuffers(0, 1, @ptrCast(&self.vertex_buffer.buffer), &.{0});
command_buffer.bindIndexBuffer(self.index_buffer.buffer, 0, .uint16);
command_buffer.bindDescriptorSets(.graphics, self.pipeline_layout, 0, self.descriptor_sets.len, &self.descriptor_sets, 0, null);
command_buffer.bindDescriptorSets(
.graphics,
self.pipeline_layout,
0,
self.descriptor_sets.len,
&self.descriptor_sets,
0,
null,
);
command_buffer.drawIndexed(@intCast(self.index_buffer.array_capacity), object_count, 0, 0, 0);
command_buffer.drawIndexed(@intCast(self.index_buffer.array_capacity), self.object_count, 0, 0, 0);
}
try command_buffer.endCommandBuffer();
const res = try self.swapchain.present(self.engine, command_buffer.handle);
_ = res;

View File

@@ -170,6 +170,11 @@ fn loadTexture(engine: *Engine, filename: []const u8, usage: Texture.Usage, temp
var img = try stbi.Image.loadFromMemory(file_buf, usage.samplesPerTexel());
defer img.deinit();
std.debug.assert(img.num_components == usage.samplesPerTexel());
if (usage == .normal) {
for (img.data) |*sample| {
sample.* = sample.* +% 128;
}
}
var texture = try Texture.init(engine, .{
.width = img.width,

View File

@@ -104,6 +104,9 @@ graphics_command_pool: vk.CommandPool,
compute_command_pool: vk.CommandPool,
transfer_command_pool: vk.CommandPool,
prng_ptr: *std.Random.Pcg,
random: std.Random,
const debug = @import("builtin").mode == .Debug;
const vk_application_info: vk.ApplicationInfo = .{
@@ -164,7 +167,7 @@ pub fn init(allocator: std.mem.Allocator, maybe_window: ?*glfw.Window) !Engine {
break :blk try base.createInstance(&.{
.p_application_info = &vk_application_info,
.enabled_layer_count = enabled_layers.len,
.enabled_layer_count = 0, //enabled_layers.len,
.pp_enabled_layer_names = enabled_layers.ptr,
.enabled_extension_count = @intCast(enabled_extensions.items.len),
.pp_enabled_extension_names = enabled_extensions.items.ptr,
@@ -345,6 +348,15 @@ pub fn init(allocator: std.mem.Allocator, maybe_window: ?*glfw.Window) !Engine {
const transfer_queue: Queue = .initAllocation(device, queue_allocations.transfer_queue);
const presentation_queue: Queue = if (maybe_window != null) .initAllocation(device, queue_allocations.presentation_queue) else undefined;
// --- CREATE AND SEED RNG -------------------------------------------------
const prng_ptr = try allocator.create(std.Random.Pcg);
errdefer allocator.destroy(prng_ptr);
const timestamp: u128 = @bitCast(std.time.nanoTimestamp());
prng_ptr.* = .init(@truncate(timestamp));
const random = prng_ptr.random();
// -------------------------------------------------------------------------
return .{
@@ -371,12 +383,17 @@ pub fn init(allocator: std.mem.Allocator, maybe_window: ?*glfw.Window) !Engine {
.graphics_command_pool = graphics_command_pool,
.compute_command_pool = compute_command_pool,
.transfer_command_pool = transfer_command_pool,
.prng_ptr = prng_ptr,
.random = random,
};
}
pub fn deinit(self: *Engine) void {
const allocator = self.vk_allocator.allocator;
allocator.destroy(self.prng_ptr);
self.device.destroyCommandPool(self.graphics_command_pool, &self.vk_allocator.interface);
self.device.destroyCommandPool(self.compute_command_pool, &self.vk_allocator.interface);
self.device.destroyCommandPool(self.transfer_command_pool, &self.vk_allocator.interface);

View File

@@ -195,16 +195,12 @@ pub fn recreate(self: *Swapchain, engine: *Engine) !void {
self.swapchain_images = new_swapchain_images;
self.image_index = res.image_index;
self.semaphore_image_acquired = semaphore_image_acquired;
std.log.debug("Swapchain recreated.", .{});
}
pub fn present(self: *Swapchain, engine: *Engine, command_buffer: vk.CommandBuffer) !PresentResult {
const device = engine.device;
const mode = &engine.mode.surface;
std.log.debug("Presenting command buffer {X}.", .{@intFromEnum(command_buffer)});
// --- WAIT FOR CURRENT FRAME TO FINISH ------------------------------------
const current_swapchain_image = &self.swapchain_images[self.image_index];

View File

@@ -11,20 +11,11 @@ const Engine = @import("engine/Engine.zig");
const Swapchain = @import("engine/Swapchain.zig");
const Game = @import("Game.zig");
pub var allocator: std.mem.Allocator = undefined;
pub var temp_allocator: std.mem.Allocator = undefined;
pub var window: *glfw.Window = undefined;
pub fn main() !void {
var gpa: std.heap.GeneralPurposeAllocator(.{}) = .init;
defer _ = gpa.deinit();
var fba: std.heap.FixedBufferAllocator = .init(&struct {
pub var buffer: [c.temp_allocator_capacity]u8 = undefined;
}.buffer);
allocator = gpa.allocator();
temp_allocator = fba.threadSafeAllocator();
const allocator = gpa.allocator();
atoms.init(allocator);
defer atoms.deinit();
@@ -44,13 +35,20 @@ pub fn main() !void {
}
glfw.windowHint(.client_api, .no_api);
window = glfw.Window.create(c.default_window_width, c.default_window_height, c.window_title, null) catch |err| {
var window = glfw.Window.create(c.default_window_width, c.default_window_height, c.window_title, null) catch |err| {
std.log.err("Could not create window", .{});
return err;
};
defer window.destroy();
window.setSizeLimits(c.min_window_width, c.min_window_height, -1, -1);
window.setInputMode(.cursor, .disabled) catch {};
if (glfw.rawMouseMotionSupported()) {
window.setInputMode(.raw_mouse_motion, true) catch {};
}
_ = window.setKeyCallback(keyCallback);
_ = window.setCursorPosCallback(cursorPosCallback);
var engine = try Engine.init(allocator, window);
defer engine.deinit();
@@ -59,7 +57,19 @@ pub fn main() !void {
defer swapchain.deinit(&engine);
var game = try Game.init(allocator, &engine, &swapchain);
defer game.deinit();
var callback_context: CallbackContext = blk: {
const cursor_last_xpos, const cursor_last_ypos = window.getCursorPos();
break :blk .{
.game = &game,
.cursor_last_xpos = cursor_last_xpos,
.cursor_last_ypos = cursor_last_ypos,
};
};
window.setUserPointer(&callback_context);
defer {
window.setUserPointer(null);
game.deinit();
}
var t1 = glfw.getTime();
while (!window.shouldClose()) {
@@ -73,3 +83,42 @@ pub fn main() !void {
std.Thread.sleep(1 * std.time.ns_per_ms);
}
}
const CallbackContext = struct {
game: *Game,
cursor_last_xpos: f64,
cursor_last_ypos: f64,
};
fn keyCallback(window: *glfw.Window, key_code: glfw.Key, _: c_int, action: glfw.Action, mods: glfw.Mods) callconv(.c) void {
const maybe_ctx = window.getUserPointer(CallbackContext);
if (key_code == .escape and action == .press and (window.getInputMode(.cursor) catch .normal) == .disabled) {
window.setInputMode(.cursor, .normal) catch {};
if (maybe_ctx) |ctx| {
const cursor_last_xpos, const cursor_last_ypos = window.getCursorPos();
ctx.cursor_last_xpos = cursor_last_xpos;
ctx.cursor_last_ypos = cursor_last_ypos;
}
return;
}
if (maybe_ctx) |ctx| {
switch (action) {
.press => ctx.game.onKeyDown(key_code, mods),
.release => ctx.game.onKeyUp(key_code, mods),
.repeat => {},
}
}
}
fn cursorPosCallback(window: *glfw.Window, cursor_xpos: f64, cursor_ypos: f64) callconv(.c) void {
const maybe_ctx = window.getUserPointer(CallbackContext);
if (maybe_ctx) |ctx| {
const dx: f32 = @floatCast(cursor_xpos - ctx.cursor_last_xpos);
const dy: f32 = @floatCast(cursor_ypos - ctx.cursor_last_ypos);
ctx.game.onMouseMove(dx, dy);
ctx.cursor_last_xpos = cursor_xpos;
ctx.cursor_last_ypos = cursor_ypos;
}
}

View File

@@ -122,7 +122,8 @@ pub const Vector2 = extern struct {
}
pub inline fn normalize(self: Vector2) Vector2 {
return .{ .vector = self.vector / @sqrt(@reduce(.Add, self.vector * self.vector)) };
const len_vector: Vector = @splat(@sqrt(@reduce(.Add, self.vector * self.vector)));
return .{ .vector = self.vector / len_vector };
}
pub inline fn dot(self: Vector2, other: Vector2) f32 {

View File

@@ -132,7 +132,8 @@ pub const Vector3 = extern struct {
}
pub inline fn normalize(self: Vector3) Vector3 {
return .{ .vector = self.vector / @sqrt(@reduce(.Add, self.vector * self.vector)) };
const len_vector: Vector = @splat(@sqrt(@reduce(.Add, self.vector * self.vector)));
return .{ .vector = self.vector / len_vector };
}
pub inline fn dot(self: Vector3, other: Vector3) f32 {

View File

@@ -140,7 +140,8 @@ pub const Vector4 = extern struct {
}
pub inline fn normalize(self: Vector4) Vector4 {
return .{ .vector = self.vector / @sqrt(@reduce(.Add, self.vector * self.vector)) };
const len_vector: Vector = @splat(@sqrt(@reduce(.Add, self.vector * self.vector)));
return .{ .vector = self.vector / len_vector };
}
pub inline fn dot(self: Vector4, other: Vector4) f32 {