Backlog/engine/core/src/math.zig

618 lines
19 KiB
Zig

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;
}