diff --git a/src/Game.zig b/src/Game.zig index edfb88d..654b160 100644 --- a/src/Game.zig +++ b/src/Game.zig @@ -15,6 +15,7 @@ const Engine = @import("engine/Engine.zig"); const GenericBuffer = @import("engine/GenericBuffer.zig").GenericBuffer; const StagingBuffer = @import("engine/StagingBuffer.zig"); const Swapchain = @import("engine/Swapchain.zig"); +const Player = @import("Player.zig"); const Interator2 = math.Interator2; const Matrix4x4 = math.Matrix4x4; const Quaternion = math.Quaternion; @@ -128,16 +129,7 @@ materials: Materials, textures: Textures, chunks: std.AutoHashMapUnmanaged([3]i16, Chunk), -camera_position: Vector3 = .init(0, 0, 1.62), -camera_pitch: f32 = 0, -camera_yaw: f32 = 0, - -input_forwards: bool = false, -input_backwards: bool = false, -input_left: bool = false, -input_right: bool = false, -input_up: bool = false, -input_down: bool = false, +player: Player, const max_textures = 1024; const max_point_lights = 1024; @@ -145,12 +137,6 @@ const max_directional_lights = 4; const chunk_descriptor_pool = 512; const camera_near_plane = 0.1; -const camera_vertical_fov_deg = 80.0; -const camera_half_vertical_fov_rad = 0.5 * camera_vertical_fov_deg * std.math.rad_per_deg; -const camera_mouse_sensitivity = 0.002; - -const player_horizontal_speed = 11.0; -const player_vertical_speed = 7.49; pub fn init(allocator: std.mem.Allocator, engine: *Engine, swapchain: *Swapchain) !Game { var materials = try Materials.init(engine, allocator); @@ -664,7 +650,7 @@ pub fn init(allocator: std.mem.Allocator, engine: *Engine, swapchain: *Swapchain } } - const camera_position = Vector3.init(0, 0, @as(f32, @floatFromInt(worldHeightI(.zero))) + 0.5 + 1.62); + const player_position = Vector3.init(0, 0, @as(f32, @floatFromInt(worldHeightI(.zero))) + 0.5); var chunk_it = chunks.iterator(); while (chunk_it.next()) |entry| { @@ -724,7 +710,7 @@ pub fn init(allocator: std.mem.Allocator, engine: *Engine, swapchain: *Swapchain .textures = textures, .chunks = chunks, - .camera_position = camera_position, + .player = .init(player_position, 0, 0), }; } @@ -765,34 +751,25 @@ pub fn deinit(self: *Game) void { } pub fn update(self: *Game, dt: f32) void { - const camera_d = Vector2 - .init( - @as(f32, @floatFromInt(@intFromBool(self.input_right))) - @as(f32, @floatFromInt(@intFromBool(self.input_left))), - @as(f32, @floatFromInt(@intFromBool(self.input_forwards))) - @as(f32, @floatFromInt(@intFromBool(self.input_backwards))), - ) - .rotate(self.camera_yaw) - .mulScalar(player_horizontal_speed) - .asVector3(player_vertical_speed * (@as(f32, @floatFromInt(@intFromBool(self.input_up))) - @as(f32, @floatFromInt(@intFromBool(self.input_down))))) - .mulScalar(dt); - - self.camera_position = Vector3.add(self.camera_position, camera_d); + self.player.update(dt); const framebuffer_size = Vector2.init( @floatFromInt(self.swapchain.extent.width), @floatFromInt(self.swapchain.extent.height), ); + const camera_position = self.player.position.add(.init(0, Player.camera_height, 0)); const camera_rotation = Quaternion.mulQuaternion( - .initRotationXY(self.camera_yaw), - .initRotationYZ(self.camera_pitch), + .initRotationXY(self.player.yaw_rad), + .initRotationYZ(self.player.pitch_rad), ); const camera_aspect_ratio = framebuffer_size.getX() / framebuffer_size.getY(); - const camera_yscale = 1.0 / @tan(camera_half_vertical_fov_rad); + const camera_yscale = 1.0 / @tan(0.5 * self.player.vertical_fov_deg * std.math.rad_per_deg); const camera_xscale = camera_yscale / camera_aspect_ratio; const matrix_ws_to_vs = Matrix4x4.mulMatrix( Matrix4x4.initRotation(camera_rotation.conjugate()), - Matrix4x4.initTranslation(self.camera_position.negate()), + Matrix4x4.initTranslation(camera_position.negate()), ); // zig fmt: off @@ -832,78 +809,26 @@ pub fn update(self: *Game, dt: f32) void { }; } -pub fn onKeyDown(self: *Game, key_code: glfw.Key, mods: glfw.Mods) void { - const no_mods = @as(c_int, @bitCast(mods)) == 0; - - if (key_code == .escape and no_mods) { - self.engine.mode.surface.window.setShouldClose(true); - } - - if (key_code == .w) { - self.input_forwards = true; - self.input_backwards = false; - } - if (key_code == .s) { - self.input_backwards = true; - self.input_forwards = false; - } - if (key_code == .a) { - self.input_left = true; - self.input_right = false; - } - if (key_code == .d) { - self.input_right = true; - self.input_left = false; - } - if (key_code == .space) { - self.input_up = true; - } - if (key_code == .left_shift) { - self.input_down = true; - } +pub fn onKeyDown(self: *Game, key: glfw.Key) void { + self.player.onKeyDown(key); } -pub fn onKeyUp(self: *Game, key_code: glfw.Key, mods: glfw.Mods) void { - _ = mods; - - if (key_code == .w) { - self.input_forwards = false; - } - if (key_code == .s) { - self.input_backwards = false; - } - if (key_code == .a) { - self.input_left = false; - } - if (key_code == .d) { - self.input_right = false; - } - if (key_code == .space) { - self.input_up = false; - } - if (key_code == .left_shift) { - self.input_down = false; - } +pub fn onKeyUp(self: *Game, key: glfw.Key) void { + self.player.onKeyUp(key); } pub fn onMouseMove(self: *Game, dx: f32, dy: f32) void { - self.camera_pitch -= dy * camera_mouse_sensitivity; - self.camera_yaw -= dx * camera_mouse_sensitivity; - - self.camera_pitch = std.math.clamp(self.camera_pitch, -0.5 * std.math.pi, 0.5 * std.math.pi); - self.camera_yaw = @mod(self.camera_yaw, 2 * std.math.pi); + self.player.onMouseMove(dx, dy); } -pub fn onMouseDown(self: *Game, button: glfw.MouseButton, mods: glfw.Mods) void { +pub fn onMouseDown(self: *Game, button: glfw.MouseButton) void { _ = self; _ = button; - _ = mods; } -pub fn onMouseUp(self: *Game, button: glfw.MouseButton, mods: glfw.Mods) void { +pub fn onMouseUp(self: *Game, button: glfw.MouseButton) void { _ = self; _ = button; - _ = mods; } fn render(self: *Game) !void { diff --git a/src/Player.zig b/src/Player.zig new file mode 100644 index 0000000..2d84ab0 --- /dev/null +++ b/src/Player.zig @@ -0,0 +1,135 @@ +const Player = @This(); +const std = @import("std"); + +const glfw = @import("zglfw"); +const math = @import("math.zig"); + +const Vector2 = math.Vector2; +const Vector3 = math.Vector3; + +const AxisState = enum { + none, + positive, + negative, + both_positive, + both_negative, + + pub fn getComponent(self: AxisState) f32 { + return switch (self) { + .none => 0, + .positive, .both_positive => 1, + .negative, .both_negative => -1, + }; + } +}; + +const Axis = struct { + state: AxisState = .none, + positive_key: glfw.Key, + negative_key: glfw.Key, + + pub fn init(positive_key: glfw.Key, negative_key: glfw.Key) Axis { + return .{ + .positive_key = positive_key, + .negative_key = negative_key, + }; + } + + pub fn onKeyDown(self: *Axis, key_code: glfw.Key) void { + if (key_code == self.positive_key) { + switch (self.state) { + .none => self.state = .positive, + .negative => self.state = .both_positive, + else => unreachable, + } + } else if (key_code == self.negative_key) { + switch (self.state) { + .none => self.state = .negative, + .positive => self.state = .both_negative, + else => unreachable, + } + } + } + + pub fn onKeyUp(self: *Axis, key_code: glfw.Key) void { + if (key_code == self.positive_key) { + switch (self.state) { + .positive => self.state = .none, + .both_positive, .both_negative => self.state = .negative, + else => unreachable, + } + } else if (key_code == self.negative_key) { + switch (self.state) { + .negative => self.state = .none, + .both_positive, .both_negative => self.state = .positive, + else => unreachable, + } + } + } + + pub fn getComponent(self: Axis) f32 { + return self.state.getComponent(); + } +}; + +position: Vector3, +pitch_rad: f32, +yaw_rad: f32, + +x_input: Axis = .init(.d, .a), +y_input: Axis = .init(.w, .s), +z_input: Axis = .init(.space, .left_shift), + +mouse_sensitivity: f32 = 0.002, +vertical_fov_deg: f32 = 80, + +pub const camera_height = 1.62; + +pub const horizontal_speed = 11.0; +pub const vertical_speed = 7.49; + +pub const min_pitch_rad = -0.5 * std.math.pi; +pub const max_pitch_rad = 0.5 * std.math.pi; + +pub fn init(position: Vector3, pitch_rad: f32, yaw_rad: f32) Player { + return .{ + .position = position, + .pitch_rad = pitch_rad, + .yaw_rad = yaw_rad, + }; +} + +pub fn onKeyDown(self: *Player, key: glfw.Key) void { + self.x_input.onKeyDown(key); + self.y_input.onKeyDown(key); + self.z_input.onKeyDown(key); +} + +pub fn onKeyUp(self: *Player, key: glfw.Key) void { + self.x_input.onKeyUp(key); + self.y_input.onKeyUp(key); + self.z_input.onKeyUp(key); +} + +pub fn onMouseMove(self: *Player, dx: f32, dy: f32) void { + self.pitch_rad = std.math.clamp(self.pitch_rad - dy * self.mouse_sensitivity, min_pitch_rad, max_pitch_rad); + self.yaw_rad = @mod(self.yaw_rad - dx * self.mouse_sensitivity, std.math.tau); +} + +pub fn update(self: *Player, dt: f32) void { + var horizontal_input_vector = Vector2 + .init(self.x_input.getComponent(), self.y_input.getComponent()) + .rotate(self.yaw_rad); + const horizontal_input_vector_len_squared = horizontal_input_vector.lenSquared(); + if (horizontal_input_vector_len_squared > 1) { + horizontal_input_vector = horizontal_input_vector.divScalar(horizontal_input_vector_len_squared); + } + const vertical_input_vector = self.z_input.getComponent(); + + const horizontal_velocity = horizontal_input_vector.mulScalar(horizontal_speed); + const vertical_velocity = vertical_input_vector * vertical_speed; + + const displacement = horizontal_velocity.asVector3(vertical_velocity).mulScalar(dt); + + self.position = Vector3.add(self.position, displacement); +} diff --git a/src/main.zig b/src/main.zig index da85d67..de04af0 100644 --- a/src/main.zig +++ b/src/main.zig @@ -97,7 +97,7 @@ const CallbackContext = struct { cursor_last_ypos: f64, }; -fn keyCallback(window: *glfw.Window, key_code: glfw.Key, _: c_int, action: glfw.Action, mods: glfw.Mods) callconv(.c) void { +fn keyCallback(window: *glfw.Window, key_code: glfw.Key, _: c_int, action: glfw.Action, _: 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) { @@ -114,8 +114,8 @@ fn keyCallback(window: *glfw.Window, key_code: glfw.Key, _: c_int, action: glfw. if (maybe_ctx) |ctx| { if (ctx.focused) { switch (action) { - .press => ctx.game.onKeyDown(key_code, mods), - .release => ctx.game.onKeyUp(key_code, mods), + .press => ctx.game.onKeyDown(key_code), + .release => ctx.game.onKeyUp(key_code), .repeat => {}, } } @@ -135,7 +135,7 @@ fn cursorPosCallback(window: *glfw.Window, cursor_xpos: f64, cursor_ypos: f64) c } } -fn mouseButtonCallback(window: *glfw.Window, button: glfw.MouseButton, action: glfw.Action, mods: glfw.Mods) callconv(.c) void { +fn mouseButtonCallback(window: *glfw.Window, button: glfw.MouseButton, action: glfw.Action, _: glfw.Mods) callconv(.c) void { const maybe_ctx = window.getUserPointer(CallbackContext); if (button == .left and action == .press and (window.getInputMode(.cursor) catch .normal) == .normal) { @@ -151,8 +151,8 @@ fn mouseButtonCallback(window: *glfw.Window, button: glfw.MouseButton, action: g if (maybe_ctx) |ctx| { if (ctx.focused) { switch (action) { - .press => ctx.game.onMouseDown(button, mods), - .release => ctx.game.onMouseUp(button, mods), + .press => ctx.game.onMouseDown(button), + .release => ctx.game.onMouseUp(button), .repeat => {}, } }