From 2e8cfd36fdc579dbb2355ae99bab2fa9ecaecfb7 Mon Sep 17 00:00:00 2001 From: Szymon Nowakowski Date: Wed, 12 Nov 2025 01:56:08 +0100 Subject: [PATCH] Add math --- src/game.zig | 29 +++-- src/math.zig | 4 + src/math/Matrix4x4.zig | 259 ++++++++++++++++++++++++++++++++++++++++ src/math/Quaternion.zig | 114 ++++++++++++++++++ src/math/Vector3.zig | 93 +++++++++++++++ src/math/Vector4.zig | 107 +++++++++++++++++ 6 files changed, 597 insertions(+), 9 deletions(-) create mode 100644 src/math.zig create mode 100644 src/math/Matrix4x4.zig create mode 100644 src/math/Quaternion.zig create mode 100644 src/math/Vector3.zig create mode 100644 src/math/Vector4.zig diff --git a/src/game.zig b/src/game.zig index 3459bef..e7f7285 100644 --- a/src/game.zig +++ b/src/game.zig @@ -10,8 +10,12 @@ const sglue = sokol.glue; const ap = @import("asset_pipeline.zig"); const main = @import("main.zig"); +const math = @import("math.zig"); const Samplers = @import("Samplers.zig"); +const Matrix4x4 = math.Matrix4x4; +const Vector3 = math.Vector3; + var show_console: bool = false; var show_demo_window: bool = false; @@ -204,19 +208,18 @@ pub fn update(dt: f64) void { sg.applyPipeline(pipeline); sg.applyBindings(bindings); - sg.applyUniforms(shaders.UB_Global_Uniforms_Vertex, sg.asRange(&shaders.GlobalUniformsVertex{ - ._MatrixVStoCS = .{ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 }, - ._MatrixWStoVS = .{ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 }, - })); + const matrix_ws_to_vs = Matrix4x4.identity; + const matrix_vs_to_cs = Matrix4x4.identity; + const ambient_light = Vector3.init(0.01, 0.01, 0.01); - sg.applyUniforms(shaders.UB_Object_Uniforms, sg.asRange(&shaders.ObjectUniforms{ - ._MatrixOStoWS = .{ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 }, - ._MatrixOStoWSNormal = .{ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 }, + sg.applyUniforms(shaders.UB_Global_Uniforms_Vertex, sg.asRange(&shaders.GlobalUniformsVertex{ + ._MatrixWStoVS = matrix_ws_to_vs.asArray(), + ._MatrixVStoCS = matrix_vs_to_cs.asArray(), })); sg.applyUniforms(shaders.UB_Global_Uniforms_Fragment, sg.asRange(&shaders.GlobalUniformsFragment{ - ._MatrixWStoVS = .{ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 }, - ._AmbientLight = .{ 0.1, 0.1, 0.1 }, + ._MatrixWStoVS = matrix_ws_to_vs.asArray(), + ._AmbientLight = ambient_light.asArray(), ._PointLightCount = 0, ._DirectionalLightCount = 0, })); @@ -225,6 +228,14 @@ pub fn update(dt: f64) void { ._TextureIndex = 0, })); + const matrix_os_to_ws = Matrix4x4.identity; + const matrix_os_to_ws_normal = Matrix4x4.identity; + + sg.applyUniforms(shaders.UB_Object_Uniforms, sg.asRange(&shaders.ObjectUniforms{ + ._MatrixOStoWS = matrix_os_to_ws.asArray(), + ._MatrixOStoWSNormal = matrix_os_to_ws_normal.asArray(), + })); + sg.draw(0, 6, 1); sg.endPass(); } diff --git a/src/math.zig b/src/math.zig new file mode 100644 index 0000000..9616e14 --- /dev/null +++ b/src/math.zig @@ -0,0 +1,4 @@ +pub const Matrix4x4 = @import("math/Matrix4x4.zig").Matrix4x4; +pub const Quaternion = @import("math/Quaternion.zig").Quaternion; +pub const Vector3 = @import("math/Vector3.zig").Vector3; +pub const Vector4 = @import("math/Vector4.zig").Vector4; diff --git a/src/math/Matrix4x4.zig b/src/math/Matrix4x4.zig new file mode 100644 index 0000000..13ec838 --- /dev/null +++ b/src/math/Matrix4x4.zig @@ -0,0 +1,259 @@ +const Vector3 = @import("Vector3.zig").Vector3; +const Vector4 = @import("Vector4.zig").Vector4; +const Quaternion = @import("Quaternion.zig").Quaternion; + +pub const Matrix4x4 = extern struct { + i: Vector4, + j: Vector4, + k: Vector4, + t: Vector4, + + pub const identity = Matrix4x4.init( + // zig fmt: off + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1, + // zig fmt: on + ); + + pub inline fn init( + // zig fmt: off + ix: f32, iy: f32, iz: f32, iw: f32, + jx: f32, jy: f32, jz: f32, jw: f32, + kx: f32, ky: f32, kz: f32, kw: f32, + tx: f32, ty: f32, tz: f32, tw: f32, + // zig fmt: on + ) Matrix4x4 { + return .{ + .i = .{ .vector = .{ ix, iy, iz, iw } }, + .j = .{ .vector = .{ jx, jy, jz, jw } }, + .k = .{ .vector = .{ kx, ky, kz, kw } }, + .t = .{ .vector = .{ tx, ty, tz, tw } }, + }; + } + + pub inline fn initVector(i: Vector4, j: Vector4, k: Vector4, t: Vector4) Matrix4x4 { + return .{ .i = i, .j = j, .k = k, .t = t }; + } + + pub inline fn initTranslation(translation: Vector3) Matrix4x4 { + return .{ + .i = .{ .vector = .{ 1, 0, 0, 0 } }, + .j = .{ .vector = .{ 0, 1, 0, 0 } }, + .k = .{ .vector = .{ 0, 0, 1, 0 } }, + .t = .{ .vector = translation.vector ++ .{1} }, + }; + } + + pub inline fn initRotation(rotation: Quaternion) Matrix4x4 { + const xx = rotation.getX() * rotation.getX(); + const xy = rotation.getX() * rotation.getY(); + const xz = rotation.getX() * rotation.getZ(); + const xw = rotation.getX() * rotation.getW(); + const yy = rotation.getY() * rotation.getY(); + const yz = rotation.getY() * rotation.getZ(); + const yw = rotation.getY() * rotation.getW(); + const zz = rotation.getZ() * rotation.getZ(); + const zw = rotation.getZ() * rotation.getW(); + + return .{ + .i = .{ .vector = .{ 1 - 2 * (yy + zz), 2 * (xy + zw), 2 * (xz - yw), 0 } }, + .j = .{ .vector = .{ 2 * (xy - zw), 1 - 2 * (xx + zz), 2 * (yz + xw), 0 } }, + .k = .{ .vector = .{ 2 * (xz + yw), 2 * (yz - xw), 1 - 2 * (xx + yy), 0 } }, + .t = .{ .vector = .{ 0, 0, 0, 1 } }, + }; + } + + pub inline fn initScale(scale: Vector3) Matrix4x4 { + return .{ + .i = .{ .vector = .{ scale.getX(), 0, 0, 0 } }, + .j = .{ .vector = .{ 0, scale.getY(), 0, 0 } }, + .k = .{ .vector = .{ 0, 0, scale.getZ(), 0 } }, + .t = .{ .vector = .{ 0, 0, 0, 1 } }, + }; + } + + pub inline fn initTranslationRotationScale(translation: Vector3, rotation: Quaternion, scale: Vector3) Matrix4x4 { + const xx = rotation.getX() * rotation.getX(); + const xy = rotation.getX() * rotation.getY(); + const xz = rotation.getX() * rotation.getZ(); + const xw = rotation.getX() * rotation.getW(); + const yy = rotation.getY() * rotation.getY(); + const yz = rotation.getY() * rotation.getZ(); + const yw = rotation.getY() * rotation.getW(); + const zz = rotation.getZ() * rotation.getZ(); + const zw = rotation.getZ() * rotation.getW(); + + const scale_x: Vector4.Vector = @splat(scale.getX()); + const scale_y: Vector4.Vector = @splat(scale.getY()); + const scale_z: Vector4.Vector = @splat(scale.getZ()); + + const rotation_i: Vector4.Vector = .{ (1 - 2 * (yy + zz)), 2 * (xy + zw), 2 * (xz - yw), 0 }; + const rotation_j: Vector4.Vector = .{ 2 * (xy - zw), (1 - 2 * (xx + zz)), 2 * (yz + xw), 0 }; + const rotation_k: Vector4.Vector = .{ 2 * (xz + yw), 2 * (yz - xw), (1 - 2 * (xx + yy)), 0 }; + + return .{ + .i = .{ .vector = scale_x * rotation_i }, + .j = .{ .vector = scale_y * rotation_j }, + .k = .{ .vector = scale_z * rotation_k }, + .t = .{ .vector = translation.vector ++ .{1} }, + }; + } + + pub inline fn add(self: Matrix4x4, other: Matrix4x4) Matrix4x4 { + return .{ + .i = .{ .vector = self.i.vector + other.i.vector }, + .j = .{ .vector = self.j.vector + other.j.vector }, + .k = .{ .vector = self.k.vector + other.k.vector }, + .t = .{ .vector = self.t.vector + other.t.vector }, + }; + } + + pub inline fn sub(self: Matrix4x4, other: Matrix4x4) Matrix4x4 { + return .{ + .i = .{ .vector = self.i.vector - other.i.vector }, + .j = .{ .vector = self.j.vector - other.j.vector }, + .k = .{ .vector = self.k.vector - other.k.vector }, + .t = .{ .vector = self.t.vector - other.t.vector }, + }; + } + + pub inline fn mulScalar(self: Matrix4x4, scalar: f32) Matrix4x4 { + const scalar_vector: Vector4.Vector = @splat(scalar); + return .{ + .i = .{ .vector = self.i.vector * scalar_vector }, + .j = .{ .vector = self.j.vector * scalar_vector }, + .k = .{ .vector = self.k.vector * scalar_vector }, + .t = .{ .vector = self.t.vector * scalar_vector }, + }; + } + + pub inline fn divScalar(self: Matrix4x4, scalar: f32) Matrix4x4 { + const inv_scalar_vector: Vector4.Vector = @splat(1 / scalar); + return .{ + .i = .{ .vector = self.i * inv_scalar_vector }, + .j = .{ .vector = self.j * inv_scalar_vector }, + .k = .{ .vector = self.k * inv_scalar_vector }, + .t = .{ .vector = self.t * inv_scalar_vector }, + }; + } + + pub inline fn negate(self: Matrix4x4) Matrix4x4 { + return .{ + .i = .{ .vector = -self.i.vector }, + .j = .{ .vector = -self.j.vector }, + .k = .{ .vector = -self.k.vector }, + .t = .{ .vector = -self.t.vector }, + }; + } + + pub inline fn mulMatrix(self: Matrix4x4, other: Matrix4x4) Matrix4x4 { + return .{ + .i = self.i.mulScalar(other.i.getX()) + .add(self.j.mulScalar(other.i.getY())) + .add(self.k.mulScalar(other.i.getZ())) + .add(self.t.mulScalar(other.i.getW())), + .j = self.i.mulScalar(other.j.getX()) + .add(self.j.mulScalar(other.j.getY())) + .add(self.k.mulScalar(other.j.getZ())) + .add(self.t.mulScalar(other.j.getW())), + .k = self.i.mulScalar(other.k.getX()) + .add(self.j.mulScalar(other.k.getY())) + .add(self.k.mulScalar(other.k.getZ())) + .add(self.t.mulScalar(other.k.getW())), + .t = self.i.mulScalar(other.t.getX()) + .add(self.j.mulScalar(other.t.getY())) + .add(self.k.mulScalar(other.t.getZ())) + .add(self.t.mulScalar(other.t.getW())), + }; + } + + pub inline fn inverseAffine(self: Matrix4x4) Matrix4x4 { + // zig fmt: off + const det = self.i.getX() * self.j.getY() * self.k.getZ() + + self.i.getY() * self.j.getZ() * self.k.getX() + + self.i.getZ() * self.j.getX() * self.k.getY() + - self.i.getX() * self.j.getZ() * self.k.getY() + - self.i.getY() * self.j.getX() * self.k.getZ() + - self.i.getZ() * self.j.getY() * self.k.getX(); + // zig fmt: on + const inv_det = 1 / det; + + const i = Vector3.init( + self.j.getY() * self.k.getZ() - self.j.getZ() * self.k.getY(), + self.i.getZ() * self.k.getY() - self.i.getY() * self.k.getZ(), + self.i.getY() * self.j.getZ() - self.i.getZ() * self.j.getY(), + ).mulScalar(inv_det); + + const j = Vector3.init( + self.j.getZ() * self.k.getX() - self.j.getX() * self.k.getZ(), + self.i.getX() * self.k.getZ() - self.i.getZ() * self.k.getX(), + self.i.getZ() * self.j.getX() - self.i.getX() * self.j.getZ(), + ).mulScalar(inv_det); + + const k = Vector3.init( + self.j.getX() * self.k.getY() - self.j.getY() * self.k.getX(), + self.i.getY() * self.k.getX() - self.i.getX() * self.k.getY(), + self.i.getX() * self.j.getY() - self.i.getY() * self.j.getX(), + ).mulScalar(inv_det); + + const t = self.i.asVector3().mulScalar(self.t.getX()) + .add(self.j.asVector3().mulScalar(self.t.getY())) + .add(self.k.asVector3().mulScalar(self.t.getZ())) + .negate(); + + return .{ + .i = .{ .vector = i ++ .{0} }, + .j = .{ .vector = j ++ .{0} }, + .k = .{ .vector = k ++ .{0} }, + .t = .{ .vector = t ++ .{1} }, + }; + } + + pub inline fn inverseTransposeAffine(self: Matrix4x4) Matrix4x4 { + // zig fmt: off + const det = self.i.getX() * self.j.getY() * self.k.getZ() + + self.i.getY() * self.j.getZ() * self.k.getX() + + self.i.getZ() * self.j.getX() * self.k.getY() + - self.i.getX() * self.j.getZ() * self.k.getY() + - self.i.getY() * self.j.getX() * self.k.getZ() + - self.i.getZ() * self.j.getY() * self.k.getX(); + // zig fmt: on + const inv_det = 1 / det; + + const i = Vector3.init( + self.j.getY() * self.k.getZ() - self.j.getZ() * self.k.getY(), + self.j.getZ() * self.k.getX() - self.j.getX() * self.k.getZ(), + self.j.getX() * self.k.getY() - self.j.getY() * self.k.getX(), + ).mulScalar(inv_det); + + const j = Vector3.init( + self.i.getZ() * self.k.getY() - self.i.getY() * self.k.getZ(), + self.i.getX() * self.k.getZ() - self.i.getZ() * self.k.getX(), + self.i.getY() * self.k.getX() - self.i.getX() * self.k.getY(), + ).mulScalar(inv_det); + + const k = Vector3.init( + self.i.getY() * self.j.getZ() - self.i.getZ() * self.j.getY(), + self.i.getZ() * self.j.getX() - self.i.getX() * self.j.getZ(), + self.i.getX() * self.j.getY() - self.i.getY() * self.j.getX(), + ).mulScalar(inv_det); + + const t = self.i.asVector3().mulScalar(self.t.getX()) + .add(self.j.asVector3().mulScalar(self.t.getY())) + .add(self.k.asVector3().mulScalar(self.t.getZ())) + .negate(); + + return .{ + .i = .{ .vector = i ++ .{0} }, + .j = .{ .vector = j ++ .{0} }, + .k = .{ .vector = k ++ .{0} }, + .t = .{ .vector = t ++ .{1} }, + }; + } + + pub inline fn asArray(self: Matrix4x4) [16]f32 { + return @bitCast(self); + } +}; diff --git a/src/math/Quaternion.zig b/src/math/Quaternion.zig new file mode 100644 index 0000000..2776637 --- /dev/null +++ b/src/math/Quaternion.zig @@ -0,0 +1,114 @@ +const std = @import("std"); + +pub const Quaternion = extern struct { + vector: Vector, + + pub const Vector = @Vector(4, f32); + + pub const zero = Quaternion.init(0, 0, 0, 0); + pub const one = Quaternion.init(1, 1, 1, 1); + pub const identity = Quaternion.init(0, 0, 0, 1); + + pub inline fn init(x: f32, y: f32, z: f32, w: f32) Quaternion { + return .{ .vector = .{ x, y, z, w } }; + } + + pub inline fn initRotationXY(angle_rad: f32) Quaternion { + const half_angle_rad = 0.5 * angle_rad; + return .{ .vector = .{ 0, 0, @sin(half_angle_rad), @cos(half_angle_rad) } }; + } + + pub inline fn initRotationXZ(angle_rad: f32) Quaternion { + const half_angle_rad = 0.5 * angle_rad; + return .{ .vector = .{ 0, -@sin(half_angle_rad), 0, @cos(half_angle_rad) } }; + } + + pub inline fn initRotationYX(angle_rad: f32) Quaternion { + const half_angle_rad = 0.5 * angle_rad; + return .{ .vector = .{ 0, 0, -@sin(half_angle_rad), @cos(half_angle_rad) } }; + } + + pub inline fn initRotationYZ(angle_rad: f32) Quaternion { + const half_angle_rad = 0.5 * angle_rad; + return .{ .vector = .{ @sin(half_angle_rad), 0, 0, @cos(half_angle_rad) } }; + } + + pub inline fn initRotationZX(angle_rad: f32) Quaternion { + const half_angle_rad = 0.5 * angle_rad; + return .{ .vector = .{ 0, @sin(half_angle_rad), 0, @cos(half_angle_rad) } }; + } + + pub inline fn initRotationZY(angle_rad: f32) Quaternion { + const half_angle_rad = 0.5 * angle_rad; + return .{ .vector = .{ -@sin(half_angle_rad), 0, 0, @cos(half_angle_rad) } }; + } + + pub inline fn getX(self: Quaternion) f32 { + return self.vector[0]; + } + + pub inline fn getY(self: Quaternion) f32 { + return self.vector[1]; + } + + pub inline fn getZ(self: Quaternion) f32 { + return self.vector[2]; + } + + pub inline fn getW(self: Quaternion) f32 { + return self.vector[3]; + } + + pub inline fn setX(self: Quaternion, x: f32) Quaternion { + const x_vector: Vector = @splat(x); + return .{ .vector = @shuffle(Vector, self, x_vector, .{ ~@as(i32, 0), 1, 2, 3 }) }; + } + + pub inline fn setY(self: Quaternion, y: f32) Quaternion { + const y_vector: Vector = @splat(y); + return .{ .vector = @shuffle(Vector, self, y_vector, .{ 0, ~@as(i32, 1), 2, 3 }) }; + } + + pub inline fn setZ(self: Quaternion, z: f32) Quaternion { + const z_vector: Vector = @splat(z); + return .{ .vector = @shuffle(Vector, self, z_vector, .{ 0, 1, ~@as(i32, 2), 3 }) }; + } + + pub inline fn setW(self: Quaternion, w: f32) Quaternion { + const w_vector: Vector = @splat(w); + return .{ .vector = @shuffle(Vector, self, w_vector, .{ 0, 1, 2, ~@as(i32, 3) }) }; + } + + pub inline fn add(self: Quaternion, other: Quaternion) Quaternion { + return .{ .vector = self.vector + other.vector }; + } + + pub inline fn sub(self: Quaternion, other: Quaternion) Quaternion { + return .{ .vector = self.vector - other.vector }; + } + + pub inline fn mul(self: Quaternion, other: Quaternion) Quaternion { + return .{ .vector = self.vector * other.vector }; + } + + pub inline fn div(self: Quaternion, other: Quaternion) Quaternion { + return .{ .vector = self.vector / other.vector }; + } + + pub inline fn mulScalar(self: Quaternion, scalar: f32) Quaternion { + const scalar_vector: Vector = @splat(scalar); + return .{ .vector = self.vector * scalar_vector }; + } + + pub inline fn divScalar(self: Quaternion, scalar: f32) Quaternion { + const scalar_vector: Vector = @splat(scalar); + return .{ .vector = self.vector / scalar_vector }; + } + + pub inline fn lerp(self: Quaternion, other: Quaternion, t: f32) Quaternion { + const s = 1.0 - t; + const t_vector: Vector = @splat(t); + const s_vector: Vector = @splat(s); + return .{ .vector = self * t_vector + other * s_vector }; + } +}; diff --git a/src/math/Vector3.zig b/src/math/Vector3.zig new file mode 100644 index 0000000..b7d2706 --- /dev/null +++ b/src/math/Vector3.zig @@ -0,0 +1,93 @@ +const Vector4 = @import("Vector4.zig").Vector4; + +pub const Vector3 = extern struct { + vector: Vector, + + pub const Vector = @Vector(3, f32); + + pub const zero = Vector3.init(0, 0, 0); + pub const one = Vector3.init(1, 1, 1); + pub const unit_x = Vector3.init(1, 0, 0); + pub const unit_y = Vector3.init(0, 1, 0); + pub const unit_z = Vector3.init(0, 0, 1); + + pub inline fn init(x: f32, y: f32, z: f32) Vector3 { + return .{ .vector = .{ x, y, z } }; + } + + pub inline fn getX(self: Vector3) f32 { + return self.vector[0]; + } + + pub inline fn getY(self: Vector3) f32 { + return self.vector[1]; + } + + pub inline fn getZ(self: Vector3) f32 { + return self.vector[2]; + } + + pub inline fn setX(self: Vector3, x: f32) Vector3 { + const x_vector: Vector = @splat(x); + return .{ .vector = @shuffle(Vector, self, x_vector, .{ ~@as(i32, 0), 1, 2 }) }; + } + + pub inline fn setY(self: Vector3, y: f32) Vector3 { + const y_vector: Vector = @splat(y); + return .{ .vector = @shuffle(Vector, self, y_vector, .{ 0, ~@as(i32, 1), 2 }) }; + } + + pub inline fn setZ(self: Vector3, z: f32) Vector3 { + const z_vector: Vector = @splat(z); + return .{ .vector = @shuffle(Vector, self, z_vector, .{ 0, 1, ~@as(i32, 2) }) }; + } + + pub inline fn add(self: Vector3, other: Vector3) Vector3 { + return .{ .vector = self.vector + other.vector }; + } + + pub inline fn sub(self: Vector3, other: Vector3) Vector3 { + return .{ .vector = self.vector - other.vector }; + } + + pub inline fn mul(self: Vector3, other: Vector3) Vector3 { + return .{ .vector = self.vector * other.vector }; + } + + pub inline fn div(self: Vector3, other: Vector3) Vector3 { + return .{ .vector = self.vector / other.vector }; + } + + pub inline fn mulScalar(self: Vector3, scalar: f32) Vector3 { + const scalar_vector: Vector = @splat(scalar); + return .{ .vector = self.vector * scalar_vector }; + } + + pub inline fn divScalar(self: Vector3, scalar: f32) Vector3 { + const scalar_vector: Vector = @splat(scalar); + return .{ .vector = self.vector / scalar_vector }; + } + + pub inline fn negate(self: Vector3) Vector3 { + return .{ .vector = -self.vector }; + } + + pub inline fn lerp(self: Vector3, other: Vector3, t: f32) Vector3 { + const s = 1.0 - t; + const t_vector: Vector = @splat(t); + const s_vector: Vector = @splat(s); + return .{ .vector = self * t_vector + other * s_vector }; + } + + pub inline fn swizzle3(self: Vector3, comptime mask: @Vector(3, i32)) Vector3 { + return .{ .vector = @shuffle(Vector, self, undefined, mask) }; + } + + pub inline fn swizzle4(self: Vector3, comptime mask: @Vector(4, i32)) Vector4 { + return .{ .vector = @shuffle(Vector4.Vector, self, undefined, mask) }; + } + + pub inline fn asArray(self: Vector3) [3]f32 { + return self.vector; + } +}; diff --git a/src/math/Vector4.zig b/src/math/Vector4.zig new file mode 100644 index 0000000..c26f751 --- /dev/null +++ b/src/math/Vector4.zig @@ -0,0 +1,107 @@ +const Vector3 = @import("Vector3.zig").Vector3; + +pub const Vector4 = extern struct { + vector: Vector, + + pub const Vector = @Vector(4, f32); + + pub const zero = Vector4.init(0, 0, 0, 0); + pub const one = Vector4.init(1, 1, 1, 1); + pub const unit_x = Vector4.init(1, 0, 0, 0); + pub const unit_y = Vector4.init(0, 1, 0, 0); + pub const unit_z = Vector4.init(0, 0, 1, 0); + pub const unit_w = Vector4.init(0, 0, 0, 1); + + pub inline fn init(x: f32, y: f32, z: f32, w: f32) Vector4 { + return .{ .vector = .{ x, y, z, w } }; + } + + pub inline fn getX(self: Vector4) f32 { + return self.vector[0]; + } + + pub inline fn getY(self: Vector4) f32 { + return self.vector[1]; + } + + pub inline fn getZ(self: Vector4) f32 { + return self.vector[2]; + } + + pub inline fn getW(self: Vector4) f32 { + return self.vector[3]; + } + + pub inline fn setX(self: Vector4, x: f32) Vector4 { + const x_vector: Vector = @splat(x); + return .{ .vector = @shuffle(Vector, self, x_vector, .{ ~@as(i32, 0), 1, 2, 3 }) }; + } + + pub inline fn setY(self: Vector4, y: f32) Vector4 { + const y_vector: Vector = @splat(y); + return .{ .vector = @shuffle(Vector, self, y_vector, .{ 0, ~@as(i32, 1), 2, 3 }) }; + } + + pub inline fn setZ(self: Vector4, z: f32) Vector4 { + const z_vector: Vector = @splat(z); + return .{ .vector = @shuffle(Vector, self, z_vector, .{ 0, 1, ~@as(i32, 2), 3 }) }; + } + + pub inline fn setW(self: Vector4, w: f32) Vector4 { + const w_vector: Vector = @splat(w); + return .{ .vector = @shuffle(Vector, self, w_vector, .{ 0, 1, 2, ~@as(i32, 3) }) }; + } + + pub inline fn add(self: Vector4, other: Vector4) Vector4 { + return .{ .vector = self.vector + other.vector }; + } + + pub inline fn sub(self: Vector4, other: Vector4) Vector4 { + return .{ .vector = self.vector - other.vector }; + } + + pub inline fn mul(self: Vector4, other: Vector4) Vector4 { + return .{ .vector = self.vector * other.vector }; + } + + pub inline fn div(self: Vector4, other: Vector4) Vector4 { + return .{ .vector = self.vector / other.vector }; + } + + pub inline fn mulScalar(self: Vector4, scalar: f32) Vector4 { + const scalar_vector: Vector = @splat(scalar); + return .{ .vector = self.vector * scalar_vector }; + } + + pub inline fn divScalar(self: Vector4, scalar: f32) Vector4 { + const scalar_vector: Vector = @splat(scalar); + return .{ .vector = self.vector / scalar_vector }; + } + + pub inline fn negate(self: Vector4) Vector4 { + return .{ .vector = -self.vector }; + } + + pub inline fn lerp(self: Vector4, other: Vector4, t: f32) Vector4 { + const s = 1.0 - t; + const t_vector: Vector = @splat(t); + const s_vector: Vector = @splat(s); + return .{ .vector = self * t_vector + other * s_vector }; + } + + pub inline fn asVector3(self: Vector4) Vector3 { + return .{ .vector = @shuffle(Vector3.Vector, self.vector, undefined, .{ 0, 1, 2 }) }; + } + + pub inline fn swizzle3(self: Vector4, comptime mask: @Vector(3, i32)) Vector3 { + return .{ .vector = @shuffle(Vector3.Vector, self.vector, undefined, mask) }; + } + + pub inline fn swizzle4(self: Vector4, comptime mask: @Vector(4, i32)) Vector4 { + return .{ .vector = @shuffle(Vector, self.vector, undefined, mask) }; + } + + pub inline fn asArray(self: Vector4) [4]f32 { + return self.vector; + } +};