From 3ef02a97e53f9ca2d156e0bd01df1dcf2649e27b Mon Sep 17 00:00:00 2001 From: duck Date: Wed, 28 May 2025 22:36:50 +0500 Subject: [PATCH] Some math functions --- src/debug_scene.zig | 110 +++++++++++++++++++++++++++++--------------- src/graphics.zig | 4 +- src/math.zig | 90 ++++++++++++++++++++++++++++++++++++ 3 files changed, 163 insertions(+), 41 deletions(-) create mode 100644 src/math.zig diff --git a/src/debug_scene.zig b/src/debug_scene.zig index 3d8d49e..e2cdb87 100644 --- a/src/debug_scene.zig +++ b/src/debug_scene.zig @@ -1,5 +1,6 @@ const std = @import("std"); const sdl = @import("sdl"); +const math = @import("math.zig"); const Transform = @import("graphics/transform.zig"); const Controller = @import("graph/controller.zig"); const Graphics = @import("graphics.zig"); @@ -43,81 +44,114 @@ const CUBE_MESH_DATA = [_]f32{ -1, -1, 1, 1, -1, -1, }; - -const OFFSETS = [_]@Vector(3, f32){ - .{ -3, 3, 0 }, - .{ 0, 3, 0 }, - .{ 3, 3, 0 }, - .{ -3, 0, 0 }, - .{ 0, 0, 0 }, - .{ 3, 0, 0 }, - .{ -3, -3, 0 }, - .{ 0, -3, 0 }, - .{ 3, -3, 0 }, +const PLANE_MESH_DATA = [_]f32{ + -1, -1, 0, + 1, 1, 0, + -1, 1, 0, + 1, 1, 0, + -1, -1, 0, + 1, -1, 0, }; -pub const Cube = struct { +pub const Player = struct { mesh: Graphics.Mesh, transform: Graphics.Transform, + velocity: @Vector(2, f32), +}; + +pub const Environment = struct { + mesh: Graphics.Mesh, }; pub fn init(controller: *Controller, graphics: *Graphics) !void { - controller.addResource(Cube{ + controller.addResource(Player{ .mesh = try graphics.loadMesh(@ptrCast(&CUBE_MESH_DATA)), - .transform = Graphics.Transform{}, + .transform = Graphics.Transform{ + .position = .{ 0, 0, 1 }, + }, + .velocity = .{ 0, 0 }, }); + controller.addResource(Environment{ + .mesh = try graphics.loadMesh(@ptrCast(&PLANE_MESH_DATA)), + }); + graphics.camera.transform = .{ + .position = .{ 0, 0, 10 }, + }; } pub fn deinit() void {} pub fn update( - cube: *Cube, - mouse: *Game.Mouse, + player: *Player, + // mouse: *Game.Mouse, keyboard: *Game.Keyboard, graphics: *Graphics, time: *Game.Time, ) void { + const MAX_VELOCITY = 16.0; + const TIME_TO_REACH_MAX_VELOCITY = 1.0 / 8.0; + + var velocity_target: @Vector(2, f32) = .{ 0, 0 }; if (keyboard.keys.is_pressed(sdl.SCANCODE_W)) { - graphics.camera.transform.translateLocal(.{ 0.0, 0.0, -5.0 * time.delta }); + velocity_target[1] += MAX_VELOCITY; } if (keyboard.keys.is_pressed(sdl.SCANCODE_S)) { - graphics.camera.transform.translateLocal(.{ 0.0, 0.0, 5.0 * time.delta }); + velocity_target[1] -= MAX_VELOCITY; } if (keyboard.keys.is_pressed(sdl.SCANCODE_D)) { - graphics.camera.transform.translateLocal(.{ 5.0 * time.delta, 0.0, 0.0 }); + velocity_target[0] += MAX_VELOCITY; } if (keyboard.keys.is_pressed(sdl.SCANCODE_A)) { - graphics.camera.transform.translateLocal(.{ -5.0 * time.delta, 0.0, 0.0 }); + velocity_target[0] -= MAX_VELOCITY; } + velocity_target = math.limitLength(velocity_target, MAX_VELOCITY); + player.velocity = math.stepVector(player.velocity, velocity_target, MAX_VELOCITY / TIME_TO_REACH_MAX_VELOCITY * time.delta); + player.transform.position[0] += player.velocity[0] * time.delta; + player.transform.position[1] += player.velocity[1] * time.delta; - 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 target_position = player.transform.position + + @Vector(3, f32){ player.velocity[0], player.velocity[1], 0 } * + @as(@Vector(3, f32), @splat(1.0 / MAX_VELOCITY)) * + @Vector(3, f32){ 0.0, 0.0, 0.0 }; + + graphics.camera.transform.position = math.lerpTimeLn( + graphics.camera.transform.position, + target_position + @Vector(3, f32){ 0.0, -3.0, 10.0 }, + time.delta, + -25, + ); + + { // Rotate camera toward player 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( + const target_rotation = Transform.combineRotations( INIT_ROTATION, - Transform.rotationToward(ROTATED_DIR, cube.transform.position - graphics.camera.transform.position, .{ .normalize_to = true }), + Transform.rotationToward( + ROTATED_DIR, + target_position - graphics.camera.transform.position, + .{ .normalize_to = true }, + ), ); + graphics.camera.transform.rotation = Transform.normalizeRotation(math.slerpTimeLn( + graphics.camera.transform.rotation, + target_rotation, + time.delta, + -2, + )); } } -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, +pub fn draw( + player: *Player, + env: *Environment, + graphics: *Graphics, +) !void { + try graphics.drawMesh(env.mesh, Graphics.Transform{ + .scale = .{ 10, 10, 10 }, }); - for (OFFSETS) |offset| { - try graphics.drawMesh(cube.mesh, Graphics.Transform{ - .position = cube.transform.position + offset, - .rotation = cube.transform.rotation, - .scale = cube.transform.scale, - }); - } + try graphics.drawMesh(player.mesh, player.transform); } diff --git a/src/graphics.zig b/src/graphics.zig index 8e0e1cd..0186229 100644 --- a/src/graphics.zig +++ b/src/graphics.zig @@ -188,9 +188,7 @@ pub fn create() GameError!Self { .window_size = .{ 1600, 900 }, .camera = Camera{ - .transform = Transform{ - .position = .{ 0.0, 0.0, 4.0 }, - }, + .transform = .{}, .near = 1.0, .far = 1024.0, .lens = .{ 1.5 * 16.0 / 9.0, 1.5 }, diff --git a/src/math.zig b/src/math.zig new file mode 100644 index 0000000..7b9928a --- /dev/null +++ b/src/math.zig @@ -0,0 +1,90 @@ +const std = @import("std"); + +/// Smooth lerp +pub inline fn lerpTime(a: anytype, b: anytype, t: f32, comptime f: f32) @TypeOf(a, b) { + @setFloatMode(.optimized); + + return lerpTimeLn(a, b, t, @log(f)); +} + +pub fn lerpTimeLn(a: anytype, b: anytype, t: f32, lnf: f32) @TypeOf(a, b) { + @setFloatMode(.optimized); + + const a_factor = @exp(lnf * t); + const b_factor = 1.0 - a_factor; + + switch (@typeInfo(@TypeOf(a, b))) { + .float => return a_factor * a + b_factor * b, + .vector => return @as(@TypeOf(a), @splat(a_factor)) * a + @as(@TypeOf(b), @splat(b_factor)) * b, + else => @compileError("Can only interpolate between vector or float values"), + } +} + +/// Spherical lerp +pub inline fn slerpTime(a: anytype, b: anytype, t: f32, comptime f: f32) @TypeOf(a, b) { + @setFloatMode(.optimized); + + return slerpTimeLn(a, b, t, @log(f)); +} + +pub fn slerpTimeLn(a: anytype, b: anytype, t: f32, lnf: f32) @TypeOf(a, b) { + @setFloatMode(.optimized); + + const cos = @reduce(.Add, a * b); + if (cos > 0.999) { + return lerpTimeLn(a, b, t, lnf); + } + + const angle = std.math.acos(cos); + + const a_angle_factor = @exp(lnf * t); + const b_angle_factor = 1.0 - a_angle_factor; + + const rev_angle_sin = 1.0 / std.math.sin(angle); + const a_sin = std.math.sin(a_angle_factor * angle); + const b_sin = std.math.sin(b_angle_factor * angle); + + const a_factor = a_sin * rev_angle_sin; + const b_factor = b_sin * rev_angle_sin; + + return @as(@TypeOf(a), @splat(a_factor)) * a + @as(@TypeOf(b), @splat(b_factor)) * b; +} + +// Step interpolation +pub fn step(a: f32, b: f32, l: f32) f32 { + @setFloatMode(.optimized); + + if (b > a) { + return @min(a + l, b); + } else { + return @max(b, a - l); + } +} + +pub fn stepVector(a: anytype, b: anytype, l: f32) @TypeOf(a, b) { + @setFloatMode(.optimized); + + return a + limitLength(b - a, l); +} + +pub fn limitLength(vector: anytype, max_length: f32) @TypeOf(vector) { + @setFloatMode(.optimized); + + const length_square = @reduce(.Add, vector * vector); + if (length_square > max_length * max_length) { + return vector * @as(@TypeOf(vector), @splat(max_length / @sqrt(length_square))); + } + return vector; +} + +pub fn length(vector: anytype) f32 { + @setFloatMode(.optimized); + + return @sqrt(dot(vector, vector)); +} + +pub fn dot(a: anytype, b: anytype) f32 { + @setFloatMode(.optimized); + + return @reduce(.Add, a * b); +}