const std = @import("std"); const zm = @import("zmath"); const math = std.math; // vector versions. pub const f32x4 = @Vector(4, f32); // __m128_int pub const f32x8 = @Vector(8, f32); // __m256_int pub const f32x16 = @Vector(16, f32); // __m512_int pub const f32x4_zero = std.mem.zeroes(@Vector(4, f32)); pub const f32x8_zero = std.mem.zeroes(@Vector(8, f32)); pub const f32x16_zero = std.mem.zeroes(@Vector(16, f32)); pub const Rayf = RayType(Vectorf); pub fn matToScalef(mat: anytype) Vectorf { const x = Vectorf.new(mat[0][0], mat[0][1], mat[0][2]).length(); const y = Vectorf.new(mat[1][0], mat[1][1], mat[1][2]).length(); const z = Vectorf.new(mat[2][0], mat[2][1], mat[2][2]).length(); return Vectorf.new(x, y, z); } pub fn RayType(comptime T: type) type { return struct { start: T, dir: T, }; } pub fn clamp(x: anytype, min: anytype, max: anytype) @TypeOf(x) { if (x < min) return min; if (x > max) return max; return x; } pub fn matFromEulerAngles(x: f32, y: f32, z: f32) Mat { return zm.matFromRollPitchYaw(y, z, x); } pub fn Radians(comptime T: type) type { return struct { value: T, pub fn fromDegrees(f: anytype) @This() { return .{ .value = f }; } }; } pub fn radians(f: anytype) @TypeOf(f) { return f * math.pi / 180.0; } pub fn fabs(x: anytype) @TypeOf(x) { if (x < 0) return -x; return x; } pub fn Vector2Type(comptime T: type, comptime typeName: []const u8) type { return extern struct { x: T = 0, y: T = 0, pub const VectorTypeName = typeName; pub const Ones = @This(){ .x = 1, .y = 1 }; pub const Zeroes = @This(){ .x = 0, .y = 0 }; pub inline fn new(x: T, y: T) @This() { return .{ .x = x, .y = y, }; } pub inline fn add(self: @This(), other: @This()) @This() { return .{ .x = self.x + other.x, .y = self.y + other.y, }; } pub inline fn sub(self: @This(), other: @This()) @This() { return .{ .x = self.x - other.x, .y = self.y - other.y, }; } pub inline fn vmul(self: @This(), other: @This()) @This() { return .{ .x = self.x * other.x, .y = self.y * other.y, }; } pub inline fn fmul(self: @This(), other: T) @This() { return .{ .x = self.x * other, .y = self.y * other, }; } pub inline fn dot(self: @This(), other: @This()) T { return self.x * other.x + self.y * other.y; } pub inline fn equals(self: @This(), other: @This()) bool { return self.x == other.x and self.y == other.y; } pub inline fn length(self: @This()) T { return std.math.sqrt(self.x * self.x + self.y * self.y); } pub inline fn swizzleYX(self: @This()) @This() { return .{ .x = self.y, .y = self.x }; } pub inline fn normalize(self: @This()) @This() { if (fabs(self.x) <= 0.0001 and fabs(self.y) <= 0.0001) { return .{ .x = 0, .y = 0 }; } const len = std.math.sqrt(self.x * self.x + self.y * self.y); if (len < 0.00001) return .{ .x = 0, .y = 0 }; return .{ .x = self.x / len, .y = self.y / len, }; } pub inline fn negate(self: @This()) @This() { return .{ .x = -self.x, .y = self.y }; } pub inline fn cross(self: @This(), other: @This()) T { return self.x * other.y - self.y * other.x; } pub inline fn removeComponent(self: @This(), other: @This()) @This() { return self.sub(other.fmul(self.dot(other))); } pub inline fn from(o: anytype) @This() { const OType: std.builtin.Type = @typeInfo(@TypeOf(o.x)); switch (@typeInfo(T)) { .int => { switch (OType) { .int => { // convert integer into integer return .{ .x = @intCast(o.x), .y = @intCast(o.y), }; }, .float => { // convert float into float return .{ .x = @intFromFloat(o.x), .y = @intFromFloat(o.y), }; }, else => { @compileError("Invalid vector type conversion"); }, } }, .float => { switch (OType) { .int => { // convert integer into float return .{ .x = @floatFromInt(o.x), .y = @floatFromInt(o.y), }; }, .float => { // convert float return .{ .x = @floatCast(o.x), .y = @floatCast(o.y), }; }, else => { // convert float into float @compileError("Invalid vector type conversion"); }, } }, else => { @compileError("Invalid vector type conversion"); }, } @compileError("Invalid vector conversion"); } }; } pub fn Vector3Type(comptime T: type, comptime typeName: []const u8) type { return extern struct { x: T = 0, y: T = 0, z: T = 0, pub const VectorTypeName = typeName; pub const Ones = @This(){ .x = 1, .y = 1, .z = 1 }; pub const Zeroes = @This(){ .x = 0, .y = 0, .z = 0 }; pub const Up = @This(){ .y = 1 }; pub const Right = @This(){ .x = 1 }; pub const Forward = @This(){ .z = 1 }; pub inline fn new(x: T, y: T, z: T) @This() { return .{ .x = x, .y = y, .z = z, }; } pub inline fn fromInt(t: anytype) @This() { return .{ .x = t, .y = t, .z = t }; } // easing functions pub inline fn drainToZero(self: @This(), o: anytype) @This() { return self.drain(o, Zeroes); } pub inline fn drain(self: @This(), o: anytype, target: anytype) @This() { return .{ .x = drainElement(self.x, o.x, target.x), .y = drainElement(self.y, o.y, target.y), .z = drainElement(self.z, o.z, target.z), }; } inline fn drainElement(a: anytype, v: anytype, t: anytype) @TypeOf(a) { if (a > t) { return @max(t, a - v); } else { return @min(t, a + v); } } // array functions. pub inline fn toArr3(self: @This()) [3]T { return .{ self.x, self.y, self.z }; } pub inline fn fromArr3(v: [3]f32) @This() { return .{ .x = v[0], .y = v[1], .z = v[2] }; } pub inline fn toZm(self: @This()) zm.Vec { return .{ self.x, self.y, self.z, 1.0 }; } pub inline fn fromZm(vec: zm.Vec) @This() { return @This().new(vec[0], vec[1], vec[2]); } pub inline fn add(self: @This(), other: @This()) @This() { return .{ .x = self.x + other.x, .y = self.y + other.y, .z = self.z + other.z, }; } pub inline fn sub(self: @This(), other: @This()) @This() { return .{ .x = self.x - other.x, .y = self.y - other.y, .z = self.z - other.z, }; } pub inline fn vmul(self: @This(), other: @This()) @This() { return .{ .x = self.x * other.x, .y = self.y * other.y, .z = self.z * other.z, }; } pub inline fn fmul(self: @This(), other: T) @This() { return .{ .x = self.x * other, .y = self.y * other, .z = self.z * other, }; } pub inline fn dot(self: @This(), other: @This()) T { return self.x * other.x + self.y * other.y + self.z * other.z; } pub inline fn equals(self: @This(), other: @This()) bool { return self.x == other.x and self.y == self.y and self.z == self.z; } pub inline fn length(self: @This()) T { return std.math.sqrt(self.x * self.x + self.z * self.z + self.y * self.y); } pub inline fn swizzleYZX(self: @This()) @This() { return .{ .x = self.y, .y = self.z, .z = self.x }; } pub inline fn swizzleYXZ(self: @This()) @This() { return .{ .x = self.y, .y = self.x, .z = self.z }; } pub inline fn swizzleZXY(self: @This()) @This() { return .{ .x = self.z, .y = self.x, .z = self.y }; } pub inline fn swizzleZYX(self: @This()) @This() { return .{ .x = self.z, .y = self.y, .z = self.x }; } pub inline fn swizzleXZY(self: @This()) @This() { return .{ .x = self.x, .y = self.z, .z = self.y }; } pub inline fn lengthXZ(self: @This()) T { return std.math.sqrt(self.x * self.x + self.z * self.z); } pub inline fn normalize(self: @This()) @This() { if (fabs(self.x) <= 0.0001 and fabs(self.y) <= 0.0001 and fabs(self.z) <= 0.0001) { return .{ .x = 0, .y = 0, .z = 0 }; } const len = std.math.sqrt(self.x * self.x + self.y * self.y + self.z * self.z); if (len < 0.00001) return .{ .x = 0, .y = 0, .z = 0 }; return .{ .x = self.x / len, .y = self.y / len, .z = self.z / len, }; } pub inline fn fromArray(o: anytype) @This() { return .{ .x = o[0], .y = o[1], .z = o[2], }; } pub inline fn clampAllAbs(self: @This(), abs: anytype) @This() { return .{ .x = std.math.clamp(self.x, -abs, abs), .y = std.math.clamp(self.y, -abs, abs), .z = std.math.clamp(self.z, -abs, abs), }; } pub inline fn cross(self: @This(), other: @This()) @This() { return .{ .x = self.y * other.z - self.z * other.y, .y = self.z * other.x - self.x * other.z, .z = self.x * other.y - self.y * other.x, }; } pub fn lerp(self: @This(), other: @This(), alpha: anytype) @This() { return .{ .x = std.math.lerp(self.x, other.x, alpha), .y = std.math.lerp(self.y, other.y, alpha), .z = std.math.lerp(self.z, other.z, alpha), }; } pub inline fn clampAll(self: @This(), lower: anytype, upper: anytype) @This() { return .{ .x = std.math.clamp(self.x, -lower, upper), .y = std.math.clamp(self.y, -lower, upper), .z = std.math.clamp(self.z, -lower, upper), }; } pub inline fn from(o: anytype) @This() { const OType: std.builtin.Type = @typeInfo(@TypeOf(o.x)); switch (@typeInfo(T)) { .Int => { switch (OType) { .Int => { // convert integer into integer return .{ .x = @intCast(o.x), .y = @intCast(o.y), .z = @intCast(o.z), }; }, .Float => { // convert float into float return .{ .x = @intFromFloat(o.x), .y = @intFromFloat(o.y), .z = @intFromFloat(o.z), }; }, else => { @compileError("Invalid vector type conversion"); }, } }, .Float => { switch (OType) { .Int => { // convert integer into float return .{ .x = @floatFromInt(o.x), .y = @floatFromInt(o.y), .z = @floatFromInt(o.z), }; }, .Float => { // convert float return .{ .x = @floatCast(o.x), .y = @floatCast(o.y), .z = @floatCast(o.z), }; }, else => { // convert float into float @compileError("Invalid vector type conversion"); }, } }, else => { @compileError("Invalid vector type conversion"); }, } @compileError("Invalid vector conversion"); } }; } pub fn Vector4Type(comptime T: type) type { return extern struct { x: T = 0, y: T = 0, z: T = 0, w: T = 0, pub const Ones = @This(){ .x = 1, .y = 1, .z = 1 }; pub const Zeroes = @This(){ .x = 0, .y = 0, .z = 0 }; pub inline fn from(o: anytype) @This() { return .{ .x = o.x, .y = o.y, .z = o.z, .w = o.w }; } pub inline fn new(x: T, y: T, z: T, w: T) @This() { return .{ .x = x, .y = y, .z = z, .w = w, }; } pub inline fn add(self: @This(), other: @This()) @This() { return .{ .x = self.x + other.x, .y = self.y + other.y, .z = self.z + other.z, .w = self.w + other.w, }; } pub inline fn sub(self: @This(), other: @This()) @This() { return .{ .x = self.x - other.x, .y = self.y - other.y, .z = self.z - other.z, .w = self.w - other.w, }; } pub inline fn normalize(self: @This()) @This() { const len = std.math.sqrt(self.x * self.x + self.y * self.y + self.z * self.z + self.w * self.w); return .{ .x = self.x / len, .y = self.y / len, .z = self.z / len, .w = self.w / len, }; } }; } pub const Vector4 = Vector4Type(f64); pub const Vector4f = Vector4Type(f32); pub const Vector = Vector3Type(f64, "Vector"); pub const Vectorf = Vector3Type(f32, "Vectorf"); pub const Vector2f = Vector2Type(f32, "Vector2f"); pub const Vector2 = Vector2Type(f64, "Vector2"); pub const Vector2i = Vector2Type(i32, "Vector2i"); pub const Vector2c = Vector2Type(c_int, "Vector2c"); pub const Vector2u = Vector2Type(u32, "Vector2u"); pub const Vector2l = Vector2Type(i64, "Vector2l"); pub const EulerAngles = Vectorf; pub const Quat = zm.Quat; pub const Mat = zm.Mat; pub const Transform = zm.Mat; pub fn matGetPosition(m: Mat) Vectorf { const vec: Vectorf = .{}; return Vectorf.fromZm(zm.mul(vec.toZm(), m)); } pub fn matToRotation(m: Mat) Rotation { const r: Rotation = .{ .quat = zm.matToQuat(m) }; return r; } pub const Rotation = struct { quat: Quat = zm.qidentity(), pub fn init() @This() { return .{}; } pub fn add(self: @This(), other: @This()) Rotation { const m1 = zm.quatToMat(self.quat); const m2 = zm.quatToMat(other.quat); const r = zm.mul(m1, m2); return .{ .quat = zm.matToQuat(r) }; } pub fn eulerX(o: f32) @This() { return .{ .quat = zm.matToQuat(zm.rotationX((o))), }; } pub fn eulerY(o: f32) @This() { return .{ .quat = zm.matToQuat(zm.rotationY((o))), }; } pub fn eulerZ(o: f32) @This() { return .{ .quat = zm.matToQuat(zm.rotationZ((o))), }; } pub fn up(self: @This()) Vectorf { return self.rotateVector(Vectorf.Up); } pub fn forward(self: @This()) Vectorf { return self.rotateVector(Vectorf.Forward); } pub fn right(self: @This()) Vectorf { return self.rotateVector(Vectorf.Right); } pub fn rotateVector(self: @This(), other: Vectorf) Vectorf { return Vectorf.fromZm( zm.mul( other.toZm(), zm.quatToMat(self.quat), ), ); } }; pub fn lerp(a: f32, b: f32, t: f32) f32 { return a + t * (b - a); } pub fn simdVec4ToVec(vec: zm.Vec) Vector4f { return .{ .x = vec[0], .y = vec[1], .z = vec[2], .w = vec[3], }; } pub fn easeLinear(c: f32, t: f32, dt: f64, rate: f32) f32 { const d = rate * @as(f32, @floatCast(dt)); if (c > t) { return @max(c - d, t); } else if (c < t) { return @min(c + d, t); } else { return t; } } pub fn rollingAverage(average: *f64, newValue: f64, sampleCount: f64) void { average.* = average.* - (average.* / sampleCount) + newValue / sampleCount; }