diff --git a/README.md b/README.md index 5c229b7..280060c 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# [zmath](https://github.com/michal-z/zig-gamedev/zmath) +# [zmath](https://github.com/zig-gamedev/zmath) SIMD math library for game developers diff --git a/build.zig b/build.zig index 89f44b7..716a128 100644 --- a/build.zig +++ b/build.zig @@ -26,7 +26,7 @@ pub fn build(b: *std.Build) void { const options_module = options_step.createModule(); const zmath = b.addModule("root", .{ - .root_source_file = b.path("src/main.zig"), + .root_source_file = b.path("src/root.zig"), .imports = &.{ .{ .name = "zmath_options", .module = options_module }, }, @@ -36,7 +36,7 @@ pub fn build(b: *std.Build) void { const tests = b.addTest(.{ .name = "zmath-tests", - .root_source_file = b.path("src/main.zig"), + .root_source_file = b.path("src/root.zig"), .target = target, .optimize = options.optimize, }); diff --git a/build.zig.zon b/build.zig.zon index 25f146b..81a1b0d 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -2,6 +2,7 @@ .name = .zmath, .fingerprint = 0xfd23d422bd223cc2, .version = "0.11.0-dev", + .minimum_zig_version = "0.14.0", .paths = .{ "build.zig", "build.zig.zon", diff --git a/src/main.zig b/src/main.zig deleted file mode 100644 index 3158a6c..0000000 --- a/src/main.zig +++ /dev/null @@ -1,16 +0,0 @@ -//-------------------------------------------------------------------------------------------------- -// -// SIMD math library for game developers -// https://github.com/michal-z/zig-gamedev/tree/main/libs/zmath -// -// See zmath.zig for more details. -// See util.zig for additional functionality. -// -//-------------------------------------------------------------------------------------------------- -pub usingnamespace @import("zmath.zig"); -pub const util = @import("util.zig"); - -// ensure transitive closure of test coverage -comptime { - _ = util; -} diff --git a/src/zmath.zig b/src/root.zig similarity index 96% rename from src/zmath.zig rename to src/root.zig index 9ff0cb5..5404c46 100644 --- a/src/zmath.zig +++ b/src/root.zig @@ -4246,9 +4246,9 @@ fn fftUnswizzle(input: []const F32x4, output: []F32x4) void { const n = index / 2; var addr = (((@as(usize, @intCast(static.swizzle_table[n & 0xff])) << 24) | - (@as(usize, @intCast(static.swizzle_table[(n >> 8) & 0xff])) << 16) | - (@as(usize, @intCast(static.swizzle_table[(n >> 16) & 0xff])) << 8) | - (@as(usize, @intCast(static.swizzle_table[(n >> 24) & 0xff])))) >> rev32) | + (@as(usize, @intCast(static.swizzle_table[(n >> 8) & 0xff])) << 16) | + (@as(usize, @intCast(static.swizzle_table[(n >> 16) & 0xff])) << 8) | + (@as(usize, @intCast(static.swizzle_table[(n >> 24) & 0xff])))) >> rev32) | ((index & 1) * rev7 * 4); f32_output[addr] = input[index][0]; addr += rev7; @@ -4530,6 +4530,147 @@ pub fn approxEqAbs(v0: anytype, v1: anytype, eps: f32) bool { return true; } +/// ============================================================================== +/// +/// Collection of useful functions building on top of, and extending, core zmath. +/// https://github.com/michal-z/zig-gamedev/tree/main/libs/zmath +/// +/// ------------------------------------------------------------------------------ +/// 1. Matrix functions +/// ------------------------------------------------------------------------------ +/// +/// As an example, in a left handed Y-up system: +/// getAxisX is equivalent to the right vector +/// getAxisY is equivalent to the up vector +/// getAxisZ is equivalent to the forward vector +/// +/// getTranslationVec(m: Mat) Vec +/// getAxisX(m: Mat) Vec +/// getAxisY(m: Mat) Vec +/// getAxisZ(m: Mat) Vec +/// +/// ============================================================================== +pub const util = struct { + pub fn getTranslationVec(m: Mat) Vec { + var _translation = m[3]; + _translation[3] = 0; + return _translation; + } + + pub fn set_TranslationVec(m: *Mat, _translation: Vec) void { + const w = m[3][3]; + m[3] = _translation; + m[3][3] = w; + } + + pub fn getScaleVec(m: Mat) Vec { + const scale_x = length3(f32x4(m[0][0], m[1][0], m[2][0], 0))[0]; + const scale_y = length3(f32x4(m[0][1], m[1][1], m[2][1], 0))[0]; + const scale_z = length3(f32x4(m[0][2], m[1][2], m[2][2], 0))[0]; + return f32x4(scale_x, scale_y, scale_z, 0); + } + + pub fn getRotationQuat(_m: Mat) Quat { + // Ortho normalize given matrix. + const c1 = normalize3(f32x4(_m[0][0], _m[1][0], _m[2][0], 0)); + const c2 = normalize3(f32x4(_m[0][1], _m[1][1], _m[2][1], 0)); + const c3 = normalize3(f32x4(_m[0][2], _m[1][2], _m[2][2], 0)); + var m = _m; + m[0][0] = c1[0]; + m[1][0] = c1[1]; + m[2][0] = c1[2]; + m[0][1] = c2[0]; + m[1][1] = c2[1]; + m[2][1] = c2[2]; + m[0][2] = c3[0]; + m[1][2] = c3[1]; + m[2][2] = c3[2]; + + // Extract rotation + return quatFromMat(m); + } + + pub fn getAxisX(m: Mat) Vec { + return normalize3(f32x4(m[0][0], m[0][1], m[0][2], 0.0)); + } + + pub fn getAxisY(m: Mat) Vec { + return normalize3(f32x4(m[1][0], m[1][1], m[1][2], 0.0)); + } + + pub fn getAxisZ(m: Mat) Vec { + return normalize3(f32x4(m[2][0], m[2][1], m[2][2], 0.0)); + } + + test "zmath.util.mat.translation" { + // zig fmt: off + const mat_data = [18]f32{ + 1.0, + 2.0, 3.0, 4.0, 5.0, + 6.0, 7.0, 8.0, 9.0, + 10.0,11.0, 12.0,13.0, + 14.0, 15.0, 16.0, 17.0, + 18.0, + }; + // zig fmt: on + const mat = loadMat(mat_data[1..]); + try expectVecApproxEqAbs(getTranslationVec(mat), f32x4(14.0, 15.0, 16.0, 0.0), 0.0001); + } + + test "zmath.util.mat.scale" { + const mat = mul(scaling(3, 4, 5), translation(6, 7, 8)); + const scale = getScaleVec(mat); + try expectVecApproxEqAbs(scale, f32x4(3.0, 4.0, 5.0, 0.0), 0.0001); + } + + test "zmath.util.mat.rotation" { + const rotate_origin = matFromRollPitchYaw(0.1, 1.2, 2.3); + const mat = mul(mul(rotate_origin, scaling(3, 4, 5)), translation(6, 7, 8)); + const rotate_get = getRotationQuat(mat); + const v0 = mul(f32x4s(1), rotate_origin); + const v1 = mul(f32x4s(1), quatToMat(rotate_get)); + try expectVecApproxEqAbs(v0, v1, 0.0001); + } + + test "zmath.util.mat.z_vec" { + const degToRad = std.math.degreesToRadians; + var z_vec = getAxisZ(identity()); + try expectVecApproxEqAbs(z_vec, f32x4(0.0, 0.0, 1.0, 0), 0.0001); + const rot_yaw = rotationY(degToRad(90)); + identity = mul(identity(), rot_yaw); + z_vec = getAxisZ(identity()); + try expectVecApproxEqAbs(z_vec, f32x4(1.0, 0.0, 0.0, 0), 0.0001); + } + + test "zmath.util.mat.y_vec" { + const degToRad = std.math.degreesToRadians; + var y_vec = getAxisY(identity()); + try expectVecApproxEqAbs(y_vec, f32x4(0.0, 1.0, 0.0, 0), 0.01); + const rot_yaw = rotationY(degToRad(90)); + identity = mul(identity(), rot_yaw); + y_vec = getAxisY(identity()); + try expectVecApproxEqAbs(y_vec, f32x4(0.0, 1.0, 0.0, 0), 0.01); + const rot_pitch = rotationX(degToRad(90)); + identity = mul(identity(), rot_pitch); + y_vec = getAxisY(identity()); + try expectVecApproxEqAbs(y_vec, f32x4(0.0, 0.0, 1.0, 0), 0.01); + } + + test "zmath.util.mat.right" { + const degToRad = std.math.degreesToRadians; + var right = getAxisX(identity()); + try expectVecApproxEqAbs(right, f32x4(1.0, 0.0, 0.0, 0), 0.01); + const rot_yaw = rotationY(degToRad(90)); + identity = mul(identity, rot_yaw); + right = getAxisX(identity()); + try expectVecApproxEqAbs(right, f32x4(0.0, 0.0, -1.0, 0), 0.01); + const rot_pitch = rotationX(degToRad(90)); + identity = mul(identity(), rot_pitch); + right = getAxisX(identity()); + try expectVecApproxEqAbs(right, f32x4(0.0, 1.0, 0.0, 0), 0.01); + } +}; // util + // ------------------------------------------------------------------------------ // This software is available under 2 licenses -- choose whichever you prefer. // ------------------------------------------------------------------------------ diff --git a/src/util.zig b/src/util.zig deleted file mode 100644 index 3a626ef..0000000 --- a/src/util.zig +++ /dev/null @@ -1,188 +0,0 @@ -// ============================================================================== -// -// Collection of useful functions building on top of, and extending, core zmath. -// https://github.com/michal-z/zig-gamedev/tree/main/libs/zmath -// -// ------------------------------------------------------------------------------ -// 1. Matrix functions -// ------------------------------------------------------------------------------ -// -// As an example, in a left handed Y-up system: -// getAxisX is equivalent to the right vector -// getAxisY is equivalent to the up vector -// getAxisZ is equivalent to the forward vector -// -// getTranslationVec(m: Mat) Vec -// getAxisX(m: Mat) Vec -// getAxisY(m: Mat) Vec -// getAxisZ(m: Mat) Vec -// -// ============================================================================== - -const zm = @import("zmath.zig"); -const std = @import("std"); -const math = std.math; -const expect = std.testing.expect; - -pub fn getTranslationVec(m: zm.Mat) zm.Vec { - var translation = m[3]; - translation[3] = 0; - return translation; -} - -pub fn setTranslationVec(m: *zm.Mat, translation: zm.Vec) void { - const w = m[3][3]; - m[3] = translation; - m[3][3] = w; -} - -pub fn getScaleVec(m: zm.Mat) zm.Vec { - const scale_x = zm.length3(zm.f32x4(m[0][0], m[1][0], m[2][0], 0))[0]; - const scale_y = zm.length3(zm.f32x4(m[0][1], m[1][1], m[2][1], 0))[0]; - const scale_z = zm.length3(zm.f32x4(m[0][2], m[1][2], m[2][2], 0))[0]; - return zm.f32x4(scale_x, scale_y, scale_z, 0); -} - -pub fn getRotationQuat(_m: zm.Mat) zm.Quat { - // Ortho normalize given matrix. - const c1 = zm.normalize3(zm.f32x4(_m[0][0], _m[1][0], _m[2][0], 0)); - const c2 = zm.normalize3(zm.f32x4(_m[0][1], _m[1][1], _m[2][1], 0)); - const c3 = zm.normalize3(zm.f32x4(_m[0][2], _m[1][2], _m[2][2], 0)); - var m = _m; - m[0][0] = c1[0]; - m[1][0] = c1[1]; - m[2][0] = c1[2]; - m[0][1] = c2[0]; - m[1][1] = c2[1]; - m[2][1] = c2[2]; - m[0][2] = c3[0]; - m[1][2] = c3[1]; - m[2][2] = c3[2]; - - // Extract rotation - return zm.quatFromMat(m); -} - -pub fn getAxisX(m: zm.Mat) zm.Vec { - return zm.normalize3(zm.f32x4(m[0][0], m[0][1], m[0][2], 0.0)); -} - -pub fn getAxisY(m: zm.Mat) zm.Vec { - return zm.normalize3(zm.f32x4(m[1][0], m[1][1], m[1][2], 0.0)); -} - -pub fn getAxisZ(m: zm.Mat) zm.Vec { - return zm.normalize3(zm.f32x4(m[2][0], m[2][1], m[2][2], 0.0)); -} - -test "zmath.util.mat.translation" { - // zig fmt: off - const mat_data = [18]f32{ - 1.0, - 2.0, 3.0, 4.0, 5.0, - 6.0, 7.0, 8.0, 9.0, - 10.0,11.0, 12.0,13.0, - 14.0, 15.0, 16.0, 17.0, - 18.0, - }; - // zig fmt: on - const mat = zm.loadMat(mat_data[1..]); - const translation = getTranslationVec(mat); - try zm.expectVecApproxEqAbs(translation, zm.f32x4(14.0, 15.0, 16.0, 0.0), 0.0001); -} - -test "zmath.util.mat.scale" { - const mat = zm.mul(zm.scaling(3, 4, 5), zm.translation(6, 7, 8)); - const scale = getScaleVec(mat); - try zm.expectVecApproxEqAbs(scale, zm.f32x4(3.0, 4.0, 5.0, 0.0), 0.0001); -} - -test "zmath.util.mat.rotation" { - const rotate_origin = zm.matFromRollPitchYaw(0.1, 1.2, 2.3); - const mat = zm.mul(zm.mul(rotate_origin, zm.scaling(3, 4, 5)), zm.translation(6, 7, 8)); - const rotate_get = getRotationQuat(mat); - const v0 = zm.mul(zm.f32x4s(1), rotate_origin); - const v1 = zm.mul(zm.f32x4s(1), zm.quatToMat(rotate_get)); - try zm.expectVecApproxEqAbs(v0, v1, 0.0001); -} - -test "zmath.util.mat.z_vec" { - const degToRad = std.math.degreesToRadians; - var identity = zm.identity(); - var z_vec = getAxisZ(identity); - try zm.expectVecApproxEqAbs(z_vec, zm.f32x4(0.0, 0.0, 1.0, 0), 0.0001); - const rot_yaw = zm.rotationY(degToRad(90)); - identity = zm.mul(identity, rot_yaw); - z_vec = getAxisZ(identity); - try zm.expectVecApproxEqAbs(z_vec, zm.f32x4(1.0, 0.0, 0.0, 0), 0.0001); -} - -test "zmath.util.mat.y_vec" { - const degToRad = std.math.degreesToRadians; - var identity = zm.identity(); - var y_vec = getAxisY(identity); - try zm.expectVecApproxEqAbs(y_vec, zm.f32x4(0.0, 1.0, 0.0, 0), 0.01); - const rot_yaw = zm.rotationY(degToRad(90)); - identity = zm.mul(identity, rot_yaw); - y_vec = getAxisY(identity); - try zm.expectVecApproxEqAbs(y_vec, zm.f32x4(0.0, 1.0, 0.0, 0), 0.01); - const rot_pitch = zm.rotationX(degToRad(90)); - identity = zm.mul(identity, rot_pitch); - y_vec = getAxisY(identity); - try zm.expectVecApproxEqAbs(y_vec, zm.f32x4(0.0, 0.0, 1.0, 0), 0.01); -} - -test "zmath.util.mat.right" { - const degToRad = std.math.degreesToRadians; - var identity = zm.identity(); - var right = getAxisX(identity); - try zm.expectVecApproxEqAbs(right, zm.f32x4(1.0, 0.0, 0.0, 0), 0.01); - const rot_yaw = zm.rotationY(degToRad(90)); - identity = zm.mul(identity, rot_yaw); - right = getAxisX(identity); - try zm.expectVecApproxEqAbs(right, zm.f32x4(0.0, 0.0, -1.0, 0), 0.01); - const rot_pitch = zm.rotationX(degToRad(90)); - identity = zm.mul(identity, rot_pitch); - right = getAxisX(identity); - try zm.expectVecApproxEqAbs(right, zm.f32x4(0.0, 1.0, 0.0, 0), 0.01); -} - -// ------------------------------------------------------------------------------ -// This software is available under 2 licenses -- choose whichever you prefer. -// ------------------------------------------------------------------------------ -// ALTERNATIVE A - MIT License -// Copyright (c) 2022 Michal Ziulek and Contributors -// Permission is hereby granted, free of charge, to any person obtaining identity copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -// ------------------------------------------------------------------------------ -// ALTERNATIVE B - Public Domain (www.unlicense.org) -// This is free and unencumbered software released into the public domain. -// Anyone is free to copy, modify, publish, use, compile, sell, or distribute this -// software, either in source code form or as identity compiled binary, for any purpose, -// commercial or non-commercial, and by any means. -// In jurisdictions that recognize copyright laws, the author or authors of this -// software dedicate any and all copyright interest in the software to the public -// domain. We make this dedication for the benefit of the public at large and to -// the detriment of our heirs and successors. We intend this dedication to be an -// overt act of relinquishment in perpetuity of all present and future rights to -// this software under copyright law. -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// ------------------------------------------------------------------------------