Some math functions
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const sdl = @import("sdl");
|
const sdl = @import("sdl");
|
||||||
|
const math = @import("math.zig");
|
||||||
const Transform = @import("graphics/transform.zig");
|
const Transform = @import("graphics/transform.zig");
|
||||||
const Controller = @import("graph/controller.zig");
|
const Controller = @import("graph/controller.zig");
|
||||||
const Graphics = @import("graphics.zig");
|
const Graphics = @import("graphics.zig");
|
||||||
@@ -43,81 +44,114 @@ const CUBE_MESH_DATA = [_]f32{
|
|||||||
-1, -1, 1,
|
-1, -1, 1,
|
||||||
1, -1, -1,
|
1, -1, -1,
|
||||||
};
|
};
|
||||||
|
const PLANE_MESH_DATA = [_]f32{
|
||||||
const OFFSETS = [_]@Vector(3, f32){
|
-1, -1, 0,
|
||||||
.{ -3, 3, 0 },
|
1, 1, 0,
|
||||||
.{ 0, 3, 0 },
|
-1, 1, 0,
|
||||||
.{ 3, 3, 0 },
|
1, 1, 0,
|
||||||
.{ -3, 0, 0 },
|
-1, -1, 0,
|
||||||
.{ 0, 0, 0 },
|
1, -1, 0,
|
||||||
.{ 3, 0, 0 },
|
|
||||||
.{ -3, -3, 0 },
|
|
||||||
.{ 0, -3, 0 },
|
|
||||||
.{ 3, -3, 0 },
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Cube = struct {
|
pub const Player = struct {
|
||||||
mesh: Graphics.Mesh,
|
mesh: Graphics.Mesh,
|
||||||
transform: Graphics.Transform,
|
transform: Graphics.Transform,
|
||||||
|
velocity: @Vector(2, f32),
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Environment = struct {
|
||||||
|
mesh: Graphics.Mesh,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn init(controller: *Controller, graphics: *Graphics) !void {
|
pub fn init(controller: *Controller, graphics: *Graphics) !void {
|
||||||
controller.addResource(Cube{
|
controller.addResource(Player{
|
||||||
.mesh = try graphics.loadMesh(@ptrCast(&CUBE_MESH_DATA)),
|
.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 deinit() void {}
|
||||||
|
|
||||||
pub fn update(
|
pub fn update(
|
||||||
cube: *Cube,
|
player: *Player,
|
||||||
mouse: *Game.Mouse,
|
// mouse: *Game.Mouse,
|
||||||
keyboard: *Game.Keyboard,
|
keyboard: *Game.Keyboard,
|
||||||
graphics: *Graphics,
|
graphics: *Graphics,
|
||||||
time: *Game.Time,
|
time: *Game.Time,
|
||||||
) void {
|
) 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)) {
|
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)) {
|
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)) {
|
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)) {
|
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 target_position = player.transform.position +
|
||||||
const scale = 1.0 / @as(f32, @floatFromInt(graphics.window_size[1]));
|
@Vector(3, f32){ player.velocity[0], player.velocity[1], 0 } *
|
||||||
cube.transform.position[0] += mouse.dx * scale * 4.0;
|
@as(@Vector(3, f32), @splat(1.0 / MAX_VELOCITY)) *
|
||||||
cube.transform.position[1] -= mouse.dy * scale * 4.0;
|
@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 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 INIT_ROTATION = Transform.rotationByAxis(.{ 1.0, 0.0, 0.0 }, std.math.pi * 0.5);
|
||||||
|
|
||||||
const ROTATED_DIR = Transform.rotateVector(ORIGIN_DIR, INIT_ROTATION);
|
const ROTATED_DIR = Transform.rotateVector(ORIGIN_DIR, INIT_ROTATION);
|
||||||
|
|
||||||
graphics.camera.transform.rotation = Transform.combineRotations(
|
const target_rotation = Transform.combineRotations(
|
||||||
INIT_ROTATION,
|
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 {
|
pub fn draw(
|
||||||
try graphics.drawMesh(cube.mesh, Graphics.Transform{
|
player: *Player,
|
||||||
.position = .{ 0.0, 0.0, 0.0 },
|
env: *Environment,
|
||||||
.rotation = cube.transform.rotation,
|
graphics: *Graphics,
|
||||||
.scale = cube.transform.scale,
|
) !void {
|
||||||
});
|
try graphics.drawMesh(env.mesh, Graphics.Transform{
|
||||||
for (OFFSETS) |offset| {
|
.scale = .{ 10, 10, 10 },
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
@@ -188,9 +188,7 @@ pub fn create() GameError!Self {
|
|||||||
.window_size = .{ 1600, 900 },
|
.window_size = .{ 1600, 900 },
|
||||||
|
|
||||||
.camera = Camera{
|
.camera = Camera{
|
||||||
.transform = Transform{
|
.transform = .{},
|
||||||
.position = .{ 0.0, 0.0, 4.0 },
|
|
||||||
},
|
|
||||||
.near = 1.0,
|
.near = 1.0,
|
||||||
.far = 1024.0,
|
.far = 1024.0,
|
||||||
.lens = .{ 1.5 * 16.0 / 9.0, 1.5 },
|
.lens = .{ 1.5 * 16.0 / 9.0, 1.5 },
|
||||||
|
90
src/math.zig
Normal file
90
src/math.zig
Normal file
@@ -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);
|
||||||
|
}
|
Reference in New Issue
Block a user