From b9a804ead659839886ae7a0cade06a584042a213 Mon Sep 17 00:00:00 2001 From: Szymon Nowakowski Date: Wed, 26 Nov 2025 15:47:02 +0100 Subject: [PATCH] It works! --- src/Game.zig | 128 ++++++++++++++++++--------------------- src/assets/Textures.zig | 5 ++ src/engine/Engine.zig | 19 +++++- src/engine/Swapchain.zig | 4 -- src/main.zig | 73 ++++++++++++++++++---- src/math/Vector2.zig | 3 +- src/math/Vector3.zig | 3 +- src/math/Vector4.zig | 3 +- 8 files changed, 150 insertions(+), 88 deletions(-) diff --git a/src/Game.zig b/src/Game.zig index aa5c9bd..b259d58 100644 --- a/src/Game.zig +++ b/src/Game.zig @@ -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; diff --git a/src/assets/Textures.zig b/src/assets/Textures.zig index b0eed68..5098a00 100644 --- a/src/assets/Textures.zig +++ b/src/assets/Textures.zig @@ -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, diff --git a/src/engine/Engine.zig b/src/engine/Engine.zig index e69467c..4f4e5b5 100644 --- a/src/engine/Engine.zig +++ b/src/engine/Engine.zig @@ -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); diff --git a/src/engine/Swapchain.zig b/src/engine/Swapchain.zig index 5126518..e10e7a4 100644 --- a/src/engine/Swapchain.zig +++ b/src/engine/Swapchain.zig @@ -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]; diff --git a/src/main.zig b/src/main.zig index c64eaaa..0bf868b 100644 --- a/src/main.zig +++ b/src/main.zig @@ -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; + } +} diff --git a/src/math/Vector2.zig b/src/math/Vector2.zig index 0f5b964..dd32912 100644 --- a/src/math/Vector2.zig +++ b/src/math/Vector2.zig @@ -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 { diff --git a/src/math/Vector3.zig b/src/math/Vector3.zig index 17ca1f7..0fb81fe 100644 --- a/src/math/Vector3.zig +++ b/src/math/Vector3.zig @@ -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 { diff --git a/src/math/Vector4.zig b/src/math/Vector4.zig index 9e9e7d4..ff7c9dc 100644 --- a/src/math/Vector4.zig +++ b/src/math/Vector4.zig @@ -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 {