diff --git a/src/math/main.zig b/src/math/main.zig index 3ec9e748..34205d06 100644 --- a/src/math/main.zig +++ b/src/math/main.zig @@ -51,6 +51,7 @@ pub const Vec2 = vec.Vec(2, f32); pub const Vec3 = vec.Vec(3, f32); pub const Vec4 = vec.Vec(4, f32); pub const Quat = q.Quat(f32); +pub const Mat2x2 = mat.Mat(2, 2, Vec2); pub const Mat3x3 = mat.Mat(3, 3, Vec3); pub const Mat4x4 = mat.Mat(4, 4, Vec4); pub const Ray = ray.Ray(Vec3); @@ -60,6 +61,7 @@ pub const Vec2h = vec.Vec(2, f16); pub const Vec3h = vec.Vec(3, f16); pub const Vec4h = vec.Vec(4, f16); pub const Quath = q.Quat(f16); +pub const Mat2x2h = mat.Mat(2, 2, Vec2h); pub const Mat3x3h = mat.Mat(3, 3, Vec3h); pub const Mat4x4h = mat.Mat(4, 4, Vec4h); pub const Rayh = ray.Ray(Vec3h); @@ -69,6 +71,7 @@ pub const Vec2d = vec.Vec(2, f64); pub const Vec3d = vec.Vec(3, f64); pub const Vec4d = vec.Vec(4, f64); pub const Quatd = q.Quat(f64); +pub const Mat2x2d = mat.Mat(2, 2, Vec2d); pub const Mat3x3d = mat.Mat(3, 3, Vec3d); pub const Mat4x4d = mat.Mat(4, 4, Vec4d); pub const Rayd = ray.Ray(Vec3d); @@ -81,6 +84,7 @@ pub const vec2FromInt = Vec2.fromInt; pub const vec3FromInt = Vec3.fromInt; pub const vec4FromInt = Vec4.fromInt; pub const quat = Quat.init; +pub const mat2x2 = Mat2x2.init; pub const mat3x3 = Mat3x3.init; pub const mat4x4 = Mat4x4.init; @@ -92,6 +96,7 @@ pub const vec2hFromInt = Vec2h.fromInt; pub const vec3hFromInt = Vec3h.fromInt; pub const vec4hFromInt = Vec4h.fromInt; pub const quath = Quath.init; +pub const mat2x2h = Mat2x2h.init; pub const mat3x3h = Mat3x3h.init; pub const mat4x4h = Mat4x4h.init; @@ -103,6 +108,7 @@ pub const vec2dFromInt = Vec2d.fromInt; pub const vec3dFromInt = Vec3d.fromInt; pub const vec4dFromInt = Vec4d.fromInt; pub const quatd = Quatd.init; +pub const mat2x2d = Mat2x2d.init; pub const mat3x3d = Mat3x3d.init; pub const mat4x4d = Mat4x4d.init; diff --git a/src/math/mat.zig b/src/math/mat.zig index 8b35fdc5..1b9725a5 100644 --- a/src/math/mat.zig +++ b/src/math/mat.zig @@ -51,6 +51,10 @@ pub fn Mat( /// Identity matrix pub const ident = switch (Matrix) { + inline math.Mat2x2, math.Mat2x2h, math.Mat2x2d => Matrix.init( + &RowVec.init(1, 0), + &RowVec.init(0, 1), + ), inline math.Mat3x3, math.Mat3x3h, math.Mat3x3d => Matrix.init( &RowVec.init(1, 0, 0), &RowVec.init(0, 1, 0), @@ -66,6 +70,70 @@ pub fn Mat( }; pub usingnamespace switch (Matrix) { + inline math.Mat2x2, math.Mat2x2h, math.Mat2x2d => struct { + /// Constructs a 2x2 matrix with the given rows. For example to write a translation + /// matrix like in the left part of this equation: + /// + /// ``` + /// |1 tx| |x | |x+y*tx| + /// |0 ty| |y=1| = |ty | + /// ``` + /// + /// You would write it with the same visual layout: + /// + /// ``` + /// const m = Mat2x2.init( + /// vec3(1, tx), + /// vec3(0, ty), + /// ); + /// ``` + /// + /// Note that Mach matrices use [column-major storage and column-vectors](https://machengine.org/engine/math/matrix-storage/). + pub inline fn init(r0: *const RowVec, r1: *const RowVec) Matrix { + return .{ .v = [_]Vec{ + Vec.init(r0.x(), r1.x()), + Vec.init(r0.y(), r1.y()), + } }; + } + + /// Returns the row `i` of the matrix. + pub inline fn row(m: *const Matrix, i: usize) RowVec { + // Note: we inline RowVec.init manually here as it is faster in debug builds. + // return RowVec.init(m.v[0].v[i], m.v[1].v[i]); + return .{ .v = .{ m.v[0].v[i], m.v[1].v[i] } }; + } + + /// Returns the column `i` of the matrix. + pub inline fn col(m: *const Matrix, i: usize) RowVec { + // Note: we inline RowVec.init manually here as it is faster in debug builds. + // return RowVec.init(m.v[i].v[0], m.v[i].v[1]); + return .{ .v = .{ m.v[i].v[0], m.v[i].v[1] } }; + } + + /// Transposes the matrix. + pub inline fn transpose(m: *const Matrix) Matrix { + return .{ .v = [_]Vec{ + Vec.init(m.v[0].v[0], m.v[1].v[0]), + Vec.init(m.v[0].v[1], m.v[1].v[1]), + } }; + } + + /// Constructs a 1D matrix which scales each dimension by the given scalar. + pub inline fn scaleScalar(t: Vec.T) Matrix { + return init( + &RowVec.init(t, 0), + &RowVec.init(0, 1), + ); + } + + /// Constructs a 1D matrix which translates coordinates by the given scalar. + pub inline fn translateScalar(t: Vec.T) Matrix { + return init( + &RowVec.init(1, t), + &RowVec.init(0, 1), + ); + } + }, inline math.Mat3x3, math.Mat3x3h, math.Mat3x3d => struct { /// Constructs a 3x3 matrix with the given rows. For example to write a translation /// matrix like in the left part of this equation: @@ -390,21 +458,26 @@ pub fn Mat( test "gpu_compatibility" { // https://www.w3.org/TR/WGSL/#alignment-and-size + try testing.expect(usize, 16).eql(@sizeOf(math.Mat2x2)); try testing.expect(usize, 48).eql(@sizeOf(math.Mat3x3)); try testing.expect(usize, 64).eql(@sizeOf(math.Mat4x4)); + try testing.expect(usize, 8).eql(@sizeOf(math.Mat2x2h)); try testing.expect(usize, 24).eql(@sizeOf(math.Mat3x3h)); try testing.expect(usize, 32).eql(@sizeOf(math.Mat4x4h)); - try testing.expect(usize, 48 * 2).eql(@sizeOf(math.Mat3x3d)); // speculative - try testing.expect(usize, 64 * 2).eql(@sizeOf(math.Mat4x4d)); // speculative + try testing.expect(usize, 32).eql(@sizeOf(math.Mat2x2d)); // speculative + try testing.expect(usize, 96).eql(@sizeOf(math.Mat3x3d)); // speculative + try testing.expect(usize, 128).eql(@sizeOf(math.Mat4x4d)); // speculative } test "zero_struct_overhead" { - // Proof that using e.g. [3]Vec4 is equal to [3]@Vector(4, f32) - try testing.expect(usize, @alignOf([3]@Vector(4, f32))).eql(@alignOf(math.Mat3x3)); + // Proof that using e.g. [3]Vec3 is equal to [3]@Vector(3, f32) + try testing.expect(usize, @alignOf([2]@Vector(2, f32))).eql(@alignOf(math.Mat2x2)); + try testing.expect(usize, @alignOf([3]@Vector(3, f32))).eql(@alignOf(math.Mat3x3)); try testing.expect(usize, @alignOf([4]@Vector(4, f32))).eql(@alignOf(math.Mat4x4)); - try testing.expect(usize, @sizeOf([3]@Vector(4, f32))).eql(@sizeOf(math.Mat3x3)); + try testing.expect(usize, @sizeOf([2]@Vector(2, f32))).eql(@sizeOf(math.Mat2x2)); + try testing.expect(usize, @sizeOf([3]@Vector(3, f32))).eql(@sizeOf(math.Mat3x3)); try testing.expect(usize, @sizeOf([4]@Vector(4, f32))).eql(@sizeOf(math.Mat4x4)); } @@ -429,7 +502,16 @@ test "init" { }); } -test "mat3x3_ident" { +test "Mat2x2_ident" { + try testing.expect(math.Mat2x2, math.Mat2x2.ident).eql(math.Mat2x2{ + .v = [_]math.Vec2{ + math.Vec2.init(1, 0), + math.Vec2.init(0, 1), + }, + }); +} + +test "Mat3x3_ident" { try testing.expect(math.Mat3x3, math.Mat3x3.ident).eql(math.Mat3x3{ .v = [_]math.Vec3{ math.Vec3.init(1, 0, 0), @@ -439,7 +521,7 @@ test "mat3x3_ident" { }); } -test "mat4x4_ident" { +test "Mat4x4_ident" { try testing.expect(math.Mat4x4, math.Mat4x4.ident).eql(math.Mat4x4{ .v = [_]math.Vec4{ math.Vec4.init(1, 0, 0, 0), @@ -450,6 +532,24 @@ test "mat4x4_ident" { }); } +test "Mat2x2_row" { + const m = math.Mat2x2.init( + &math.vec2(0, 1), + &math.vec2(2, 3), + ); + try testing.expect(math.Vec2, math.vec2(0, 1)).eql(m.row(0)); + try testing.expect(math.Vec2, math.vec2(2, 3)).eql(m.row(@TypeOf(m).rows - 1)); +} + +test "Mat2x2_col" { + const m = math.Mat2x2.init( + &math.vec2(0, 1), + &math.vec2(2, 3), + ); + try testing.expect(math.Vec2, math.vec2(0, 2)).eql(m.col(0)); + try testing.expect(math.Vec2, math.vec2(1, 3)).eql(m.col(@TypeOf(m).cols - 1)); +} + test "Mat3x3_row" { const m = math.Mat3x3.init( &math.vec3(0, 1, 2), @@ -498,6 +598,17 @@ test "Mat4x4_col" { try testing.expect(math.Vec4, math.vec4(3, 7, 11, 15)).eql(m.col(@TypeOf(m).cols - 1)); } +test "Mat2x2_transpose" { + const m = math.Mat2x2.init( + &math.vec2(0, 1), + &math.vec2(2, 3), + ); + try testing.expect(math.Mat2x2, math.Mat2x2.init( + &math.vec2(0, 2), + &math.vec2(1, 3), + )).eql(m.transpose()); +} + test "Mat3x3_transpose" { const m = math.Mat3x3.init( &math.vec3(0, 1, 2), @@ -526,6 +637,14 @@ test "Mat4x4_transpose" { )).eql(m.transpose()); } +test "Mat2x2_scaleScalar" { + const m = math.Mat2x2.scaleScalar(2); + try testing.expect(math.Mat2x2, math.Mat2x2.init( + &math.vec2(2, 0), + &math.vec2(0, 1), + )).eql(m); +} + test "Mat3x3_scale" { const m = math.Mat3x3.scale(math.vec2(2, 3)); try testing.expect(math.Mat3x3, math.Mat3x3.init( @@ -592,6 +711,14 @@ test "Mat3x3_translateScalar" { )).eql(m); } +test "Mat2x2_translateScalar" { + const m = math.Mat2x2.translateScalar(2); + try testing.expect(math.Mat2x2, math.Mat2x2.init( + &math.vec2(1, 2), + &math.vec2(0, 1), + )).eql(m); +} + test "Mat4x4_translateScalar" { const m = math.Mat4x4.translateScalar(2); try testing.expect(math.Mat4x4, math.Mat4x4.init( @@ -612,6 +739,27 @@ test "Mat4x4_translation" { try testing.expect(math.Vec3, math.vec3(2, 3, 4)).eql(m.translation()); } +test "Mat2x2_mulVec_vec2_ident" { + const v = math.Vec2.splat(1); + const ident = math.Mat2x2.ident; + const expected = v; + var m = math.Mat2x2.mulVec(&ident, &v); + + try testing.expect(math.Vec2, expected).eql(m); +} + +test "Mat2x2_mulVec_vec2" { + const v = math.Vec2.splat(1); + const mat = math.Mat2x2.init( + &math.vec2(2, 0), + &math.vec2(0, 2), + ); + + const m = math.Mat2x2.mulVec(&mat, &v); + const expected = math.vec2(2, 2); + try testing.expect(math.Vec2, expected).eql(m); +} + test "Mat3x3_mulVec_vec3_ident" { const v = math.Vec3.splat(1); const ident = math.Mat3x3.ident; @@ -648,6 +796,24 @@ test "Mat4x4_mulVec_vec4" { try testing.expect(math.Vec4, expected).eql(m); } +test "Mat2x2_mul" { + const a = math.Mat2x2.init( + &math.vec2(4, 2), + &math.vec2(7, 9), + ); + const b = math.Mat2x2.init( + &math.vec2(5, -7), + &math.vec2(6, -3), + ); + const c = math.Mat2x2.mul(&a, &b); + + const expected = math.Mat2x2.init( + &math.vec2(32, -34), + &math.vec2(89, -76), + ); + try testing.expect(math.Mat2x2, expected).eql(c); +} + test "Mat3x3_mul" { const a = math.Mat3x3.init( &math.vec3(4, 2, -3),