diff --git a/src/data/keystore.zig b/src/data/keystore.zig new file mode 100644 index 0000000..6411dd8 --- /dev/null +++ b/src/data/keystore.zig @@ -0,0 +1,68 @@ +pub fn KeyStore(comptime key_type: type, comptime buf_size: usize, comptime zero: key_type) type { + return struct { + just_pressed: [buf_size]key_type = .{zero} ** buf_size, + pressed: [buf_size]key_type = .{zero} ** buf_size, + just_released: [buf_size]key_type = .{zero} ** buf_size, + + const Self = @This(); + pub fn reset(self: *Self) void { + self.just_pressed = .{zero} ** buf_size; + self.just_released = .{zero} ** buf_size; + } + pub fn press(self: *Self, code: key_type) void { + for (self.pressed) |pressed| { + if (pressed == code) return; + } + for (&self.pressed) |*pressed| { + if (pressed.* == zero) { + pressed.* = code; + break; + } + } + for (self.just_pressed) |just_pressed| { + if (just_pressed == code) return; + } + for (&self.just_pressed) |*just_pressed| { + if (just_pressed.* == zero) { + just_pressed.* = code; + break; + } + } + } + pub fn release(self: *Self, code: key_type) void { + for (&self.pressed) |*pressed| { + if (pressed.* == code) { + pressed.* = zero; + break; + } + } + for (self.just_released) |just_released| { + if (just_released == code) return; + } + for (&self.just_released) |*just_released| { + if (just_released.* == zero) { + just_released.* = code; + break; + } + } + } + pub fn is_just_pressed(self: *Self, code: key_type) bool { + for (self.just_pressed) |just_pressed| { + if (just_pressed == code) return true; + } + return false; + } + pub fn is_pressed(self: *Self, code: key_type) bool { + for (self.pressed) |pressed| { + if (pressed == code) return true; + } + return false; + } + pub fn is_just_released(self: *Self, code: key_type) bool { + for (self.just_released) |just_released| { + if (just_released == code) return true; + } + return false; + } + }; +} diff --git a/src/debug_scene.zig b/src/debug_scene.zig index 70eba32..3d8d49e 100644 --- a/src/debug_scene.zig +++ b/src/debug_scene.zig @@ -1,5 +1,6 @@ const std = @import("std"); const sdl = @import("sdl"); +const Transform = @import("graphics/transform.zig"); const Controller = @import("graph/controller.zig"); const Graphics = @import("graphics.zig"); const Game = @import("game.zig"); @@ -70,35 +71,48 @@ pub fn init(controller: *Controller, graphics: *Graphics) !void { pub fn deinit() void {} pub fn update( + cube: *Cube, mouse: *Game.Mouse, keyboard: *Game.Keyboard, graphics: *Graphics, time: *Game.Time, ) void { - if (keyboard.is_pressed(sdl.SCANCODE_W)) { - graphics.camera.transform.translateLocal(.{ 0.0, 0.0, 5.0 * time.delta }); - } - if (keyboard.is_pressed(sdl.SCANCODE_S)) { + if (keyboard.keys.is_pressed(sdl.SCANCODE_W)) { graphics.camera.transform.translateLocal(.{ 0.0, 0.0, -5.0 * time.delta }); } - if (keyboard.is_pressed(sdl.SCANCODE_D)) { + if (keyboard.keys.is_pressed(sdl.SCANCODE_S)) { + graphics.camera.transform.translateLocal(.{ 0.0, 0.0, 5.0 * time.delta }); + } + if (keyboard.keys.is_pressed(sdl.SCANCODE_D)) { graphics.camera.transform.translateLocal(.{ 5.0 * time.delta, 0.0, 0.0 }); } - if (keyboard.is_pressed(sdl.SCANCODE_A)) { + if (keyboard.keys.is_pressed(sdl.SCANCODE_A)) { graphics.camera.transform.translateLocal(.{ -5.0 * time.delta, 0.0, 0.0 }); } - if (@abs(mouse.dx) < 0.01 and @abs(mouse.dy) < 0.01) return; + if (mouse.buttons.is_pressed(sdl.BUTTON_LEFT)) { + const scale = 1.0 / @as(f32, @floatFromInt(graphics.window_size[1])); + cube.transform.position[0] += mouse.dx * scale * 4.0; + cube.transform.position[1] -= mouse.dy * scale * 4.0; - const delta, const length = Graphics.Transform.extractNormal(.{ mouse.dy, mouse.dx, 0.0 }); - const rot = Graphics.Transform.rotationByAxis( - delta, - length * std.math.pi / @as(f32, @floatFromInt(graphics.window_size[1])) * 2.0, - ); - graphics.camera.transform.rotateLocal(rot); + const ORIGIN_DIR = @Vector(3, f32){ 0.0, 0.0, -1.0 }; + const INIT_ROTATION = Transform.rotationByAxis(.{ 1.0, 0.0, 0.0 }, std.math.pi * 0.5); + + const ROTATED_DIR = Transform.rotateVector(ORIGIN_DIR, INIT_ROTATION); + + graphics.camera.transform.rotation = Transform.combineRotations( + INIT_ROTATION, + Transform.rotationToward(ROTATED_DIR, cube.transform.position - graphics.camera.transform.position, .{ .normalize_to = true }), + ); + } } pub fn draw(cube: *Cube, graphics: *Graphics) !void { + try graphics.drawMesh(cube.mesh, Graphics.Transform{ + .position = .{ 0.0, 0.0, 0.0 }, + .rotation = cube.transform.rotation, + .scale = cube.transform.scale, + }); for (OFFSETS) |offset| { try graphics.drawMesh(cube.mesh, Graphics.Transform{ .position = cube.transform.position + offset, diff --git a/src/game.zig b/src/game.zig index 83ad00b..7ae0045 100644 --- a/src/game.zig +++ b/src/game.zig @@ -27,6 +27,7 @@ pub fn init(alloc: std.mem.Allocator) GameError!Self { var controller = try graph.getController(); controller.addResource(graphics); controller.addResource(Mouse{ + .buttons = .{}, .x = 0.0, .y = 0.0, .dx = 0.0, @@ -106,7 +107,7 @@ fn processEvents( ) GameError!void { mouse.dx = 0.0; mouse.dy = 0.0; - keyboard.reset(); + keyboard.keys.reset(); sdl.PumpEvents(); while (true) { @@ -131,11 +132,19 @@ fn processEvents( }, sdl.EVENT_KEY_DOWN => { if (event.key.windowID != sdl.GetWindowID(graphics.window)) continue; - keyboard.press(event.key.scancode); + keyboard.keys.press(event.key.scancode); }, sdl.EVENT_KEY_UP => { if (event.key.windowID != sdl.GetWindowID(graphics.window)) continue; - keyboard.release(event.key.scancode); + keyboard.keys.release(event.key.scancode); + }, + sdl.EVENT_MOUSE_BUTTON_DOWN => { + if (event.button.windowID != sdl.GetWindowID(graphics.window)) continue; + mouse.buttons.press(event.button.button); + }, + sdl.EVENT_MOUSE_BUTTON_UP => { + if (event.button.windowID != sdl.GetWindowID(graphics.window)) continue; + mouse.buttons.release(event.button.button); }, else => {}, } diff --git a/src/graphics.zig b/src/graphics.zig index 47a4fe5..8e0e1cd 100644 --- a/src/graphics.zig +++ b/src/graphics.zig @@ -189,7 +189,7 @@ pub fn create() GameError!Self { .camera = Camera{ .transform = Transform{ - .position = .{ 0.0, 0.0, -4.0 }, + .position = .{ 0.0, 0.0, 4.0 }, }, .near = 1.0, .far = 1024.0, diff --git a/src/graphics/camera.zig b/src/graphics/camera.zig index 69b5459..3cd7fdb 100644 --- a/src/graphics/camera.zig +++ b/src/graphics/camera.zig @@ -15,10 +15,10 @@ pub fn matrix(camera: Camera) @Vector(16, f32) { const zz = camera.far * fnmod; const wz = -camera.near * camera.far * fnmod; const projection = @Vector(16, f32){ - xx, 0, 0, 0, - 0, yy, 0, 0, - 0, 0, zz, wz, - 0, 0, 1, 0, + xx, 0, 0, 0, + 0, yy, 0, 0, + 0, 0, -zz, wz, + 0, 0, -1, 0, }; return Transform.multiplyMatrix(projection, camera.transform.inverseMatrix()); } diff --git a/src/graphics/presets.zig b/src/graphics/presets.zig index b2ebb9c..d9673f0 100644 --- a/src/graphics/presets.zig +++ b/src/graphics/presets.zig @@ -20,5 +20,10 @@ pub const DEPTH_ENABLED = sdl.GPUDepthStencilState{ pub const RASTERIZER_CULL = sdl.GPURasterizerState{ .cull_mode = sdl.GPU_CULLMODE_BACK, .fill_mode = sdl.GPU_FILLMODE_FILL, - .front_face = sdl.GPU_FRONTFACE_CLOCKWISE, + .front_face = sdl.GPU_FRONTFACE_COUNTER_CLOCKWISE, +}; + +pub const RASTERIZER_NO_CULL = sdl.GPURasterizerState{ + .cull_mode = sdl.GPU_CULLMODE_NONE, + .fill_mode = sdl.GPU_FILLMODE_FILL, }; diff --git a/src/graphics/transform.zig b/src/graphics/transform.zig index d972173..a314711 100644 --- a/src/graphics/transform.zig +++ b/src/graphics/transform.zig @@ -58,10 +58,16 @@ pub fn translate(transform: *Transform, translation: Position) void { pub fn translateLocal(transform: *Transform, translation: Position) void { @setFloatMode(.optimized); - const a = transform.rotation[0]; - const b = transform.rotation[1]; - const c = transform.rotation[2]; - const d = transform.rotation[3]; + transform.position += rotateVector(translation, transform.rotation); +} + +pub fn rotateVector(vector: Position, rotation: Rotation) Position { + @setFloatMode(.optimized); + + const a = rotation[0]; + const b = rotation[1]; + const c = rotation[2]; + const d = rotation[3]; const s = 2.0 / (a * a + b * b + c * c + d * d); const bs = b * s; @@ -78,9 +84,11 @@ pub fn translateLocal(transform: *Transform, translation: Position) void { const cd = c * ds; const dd = d * ds; - transform.position[0] += translation[0] * (1 - cc - dd) + translation[1] * (bc - ad) + translation[2] * (bd + ac); - transform.position[1] += translation[0] * (bc + ad) + translation[1] * (1 - bb - dd) + translation[2] * (cd - ab); - transform.position[2] += translation[0] * (bd - ac) + translation[1] * (cd + ab) + translation[2] * (1 - bb - cc); + return .{ + vector[0] * (1 - cc - dd) + vector[1] * (bc - ad) + vector[2] * (bd + ac), + vector[0] * (bc + ad) + vector[1] * (1 - bb - dd) + vector[2] * (cd - ab), + vector[0] * (bd - ac) + vector[1] * (cd + ab) + vector[2] * (1 - bb - cc), + }; } pub fn rotate(transform: *Transform, rotation: Rotation) void { @@ -95,11 +103,49 @@ pub fn rotateLocal(transform: *Transform, rotation: Rotation) void { transform.rotation = normalizeRotation(combineRotations(rotation, transform.rotation)); } +pub fn rotateByAxis(transform: *Transform, axis: Position, angle: f32) void { + transform.rotate(rotationByAxis(axis, angle)); +} + +pub fn rotateToward(transform: *Transform, target: Position, origin_norm: Position) void { + @setFloatMode(.optimized); + + transform.rotation = rotationToward(origin_norm, target - transform.position, .{ .normalize_to = true }); +} + +const RotationTowardOptions = struct { + normalize_from: bool = false, + normalize_to: bool = false, +}; +pub fn rotationToward(from: Position, to: Position, comptime options: RotationTowardOptions) Rotation { + @setFloatMode(.optimized); + + const from_norm = if (options.normalize_from) extractNormal(from)[0] else from; + const to_norm = if (options.normalize_to) extractNormal(to)[0] else to; + + const combined = combineRotations(.{ + 0.0, to_norm[0], to_norm[1], to_norm[2], + }, .{ + 0.0, from_norm[0], from_norm[1], from_norm[2], + }); + return normalizeRotation(.{ + 1 - combined[0], + combined[1], + combined[2], + combined[3], + }); +} + pub fn normalizeRotation(r: Rotation) Rotation { @setFloatMode(.optimized); - const length_inverse = 1.0 / @sqrt(r[0] * r[0] + r[1] * r[1] + r[2] * r[2] + r[3] * r[3]); - return r * @as(Rotation, @splat(length_inverse)); + const length = @sqrt(r[0] * r[0] + r[1] * r[1] + r[2] * r[2] + r[3] * r[3]); + if (length <= 1e-15) { + return .{ 1.0, 0.0, 0.0, 0.0 }; + } else { + const length_inverse = 1.0 / length; + return r * @as(Rotation, @splat(length_inverse)); + } } pub fn combineRotations(a: Rotation, b: Rotation) Rotation { @@ -113,11 +159,11 @@ pub fn combineRotations(a: Rotation, b: Rotation) Rotation { }; } -pub fn rotationByAxis(axis: Position, rotation: f32) Rotation { +pub fn rotationByAxis(axis: Position, angle: f32) Rotation { @setFloatMode(.optimized); - const cos = std.math.cos(rotation * 0.5); - const sin = std.math.sin(rotation * 0.5); + const cos = std.math.cos(angle * 0.5); + const sin = std.math.sin(angle * 0.5); return .{ cos, sin * axis[0], sin * axis[1], sin * axis[2] }; } diff --git a/src/keyboard.zig b/src/keyboard.zig index a35eb96..a33c18d 100644 --- a/src/keyboard.zig +++ b/src/keyboard.zig @@ -1,69 +1,4 @@ const sdl = @import("sdl"); +const key_store = @import("data/keystore.zig"); -const BUFFER_SIZE = 16; -const ZERO = sdl.SCANCODE_UNKNOWN; - -just_pressed: [BUFFER_SIZE]sdl.Scancode = .{ZERO} ** BUFFER_SIZE, -pressed: [BUFFER_SIZE]sdl.Scancode = .{ZERO} ** BUFFER_SIZE, -just_released: [BUFFER_SIZE]sdl.Scancode = .{ZERO} ** BUFFER_SIZE, - -const Self = @This(); -pub fn reset(self: *Self) void { - self.just_pressed = .{ZERO} ** BUFFER_SIZE; - self.just_released = .{ZERO} ** BUFFER_SIZE; -} -pub fn press(self: *Self, code: sdl.Scancode) void { - for (self.pressed) |pressed| { - if (pressed == code) return; - } - for (&self.pressed) |*pressed| { - if (pressed.* == ZERO) { - pressed.* = code; - break; - } - } - for (self.just_pressed) |just_pressed| { - if (just_pressed == code) return; - } - for (&self.just_pressed) |*just_pressed| { - if (just_pressed.* == ZERO) { - just_pressed.* = code; - break; - } - } -} -pub fn release(self: *Self, code: sdl.Scancode) void { - for (&self.pressed) |*pressed| { - if (pressed.* == code) { - pressed.* = ZERO; - break; - } - } - for (self.just_released) |just_released| { - if (just_released == code) return; - } - for (&self.just_released) |*just_released| { - if (just_released.* == ZERO) { - just_released.* = code; - break; - } - } -} -pub fn is_just_pressed(self: *Self, code: sdl.Scancode) bool { - for (self.just_pressed) |just_pressed| { - if (just_pressed == code) return true; - } - return false; -} -pub fn is_pressed(self: *Self, code: sdl.Scancode) bool { - for (self.pressed) |pressed| { - if (pressed == code) return true; - } - return false; -} -pub fn is_just_released(self: *Self, code: sdl.Scancode) bool { - for (self.just_released) |just_released| { - if (just_released == code) return true; - } - return false; -} +keys: key_store.KeyStore(sdl.Scancode, 16, sdl.SCANCODE_UNKNOWN) = .{}, diff --git a/src/mouse.zig b/src/mouse.zig index 32e7a8a..83cf9c9 100644 --- a/src/mouse.zig +++ b/src/mouse.zig @@ -1,3 +1,8 @@ +const sdl = @import("sdl"); +const key_store = @import("data/keystore.zig"); + +buttons: key_store.KeyStore(@TypeOf(sdl.BUTTON_LEFT), 4, 0), + x: f32, y: f32, dx: f32,