···11+//--------------------------------------------------------------------------------------------------
22+//
33+// SIMD math library for game developers
44+// https://github.com/michal-z/zig-gamedev/tree/main/libs/zmath
55+//
66+// See zmath.zig for more details.
77+// See util.zig for additional functionality.
88+//
99+//--------------------------------------------------------------------------------------------------
1010+pub usingnamespace @import("zmath.zig");
1111+pub const util = @import("util.zig");
1212+1313+// ensure transitive closure of test coverage
1414+comptime {
1515+ _ = util;
1616+}
+182
libs/zmath/src/util.zig
···11+// ==============================================================================
22+//
33+// Collection of useful functions building on top of, and extending, core zmath.
44+// https://github.com/michal-z/zig-gamedev/tree/main/libs/zmath
55+//
66+// ------------------------------------------------------------------------------
77+// 1. Matrix functions
88+// ------------------------------------------------------------------------------
99+//
1010+// As an example, in a left handed Y-up system:
1111+// getAxisX is equivalent to the right vector
1212+// getAxisY is equivalent to the up vector
1313+// getAxisZ is equivalent to the forward vector
1414+//
1515+// getTranslationVec(m: Mat) Vec
1616+// getAxisX(m: Mat) Vec
1717+// getAxisY(m: Mat) Vec
1818+// getAxisZ(m: Mat) Vec
1919+//
2020+// ==============================================================================
2121+2222+const zm = @import("zmath.zig");
2323+const std = @import("std");
2424+const math = std.math;
2525+const expect = std.testing.expect;
2626+2727+pub fn getTranslationVec(m: zm.Mat) zm.Vec {
2828+ var translation = m[3];
2929+ translation[3] = 0;
3030+ return translation;
3131+}
3232+3333+pub fn getScaleVec(m: zm.Mat) zm.Vec {
3434+ const scale_x = zm.length3(zm.f32x4(m[0][0], m[1][0], m[2][0], 0))[0];
3535+ const scale_y = zm.length3(zm.f32x4(m[0][1], m[1][1], m[2][1], 0))[0];
3636+ const scale_z = zm.length3(zm.f32x4(m[0][2], m[1][2], m[2][2], 0))[0];
3737+ return zm.f32x4(scale_x, scale_y, scale_z, 0);
3838+}
3939+4040+pub fn getRotationQuat(_m: zm.Mat) zm.Quat {
4141+ // Ortho normalize given matrix.
4242+ const c1 = zm.normalize3(zm.f32x4(_m[0][0], _m[1][0], _m[2][0], 0));
4343+ const c2 = zm.normalize3(zm.f32x4(_m[0][1], _m[1][1], _m[2][1], 0));
4444+ const c3 = zm.normalize3(zm.f32x4(_m[0][2], _m[1][2], _m[2][2], 0));
4545+ var m = _m;
4646+ m[0][0] = c1[0];
4747+ m[1][0] = c1[1];
4848+ m[2][0] = c1[2];
4949+ m[0][1] = c2[0];
5050+ m[1][1] = c2[1];
5151+ m[2][1] = c2[2];
5252+ m[0][2] = c3[0];
5353+ m[1][2] = c3[1];
5454+ m[2][2] = c3[2];
5555+5656+ // Extract rotation
5757+ return zm.quatFromMat(m);
5858+}
5959+6060+pub fn getAxisX(m: zm.Mat) zm.Vec {
6161+ return zm.normalize3(zm.f32x4(m[0][0], m[0][1], m[0][2], 0.0));
6262+}
6363+6464+pub fn getAxisY(m: zm.Mat) zm.Vec {
6565+ return zm.normalize3(zm.f32x4(m[1][0], m[1][1], m[1][2], 0.0));
6666+}
6767+6868+pub fn getAxisZ(m: zm.Mat) zm.Vec {
6969+ return zm.normalize3(zm.f32x4(m[2][0], m[2][1], m[2][2], 0.0));
7070+}
7171+7272+test "zmath.util.mat.translation" {
7373+ // zig fmt: off
7474+ const mat_data = [18]f32{
7575+ 1.0,
7676+ 2.0, 3.0, 4.0, 5.0,
7777+ 6.0, 7.0, 8.0, 9.0,
7878+ 10.0,11.0, 12.0,13.0,
7979+ 14.0, 15.0, 16.0, 17.0,
8080+ 18.0,
8181+ };
8282+ // zig fmt: on
8383+ const mat = zm.loadMat(mat_data[1..]);
8484+ const translation = getTranslationVec(mat);
8585+ try expect(zm.approxEqAbs(translation, zm.f32x4(14.0, 15.0, 16.0, 0.0), 0.0001));
8686+}
8787+8888+test "zmath.util.mat.scale" {
8989+ const mat = zm.mul(zm.scaling(3, 4, 5), zm.translation(6, 7, 8));
9090+ const scale = getScaleVec(mat);
9191+ try expect(zm.approxEqAbs(scale, zm.f32x4(3.0, 4.0, 5.0, 0.0), 0.0001));
9292+}
9393+9494+test "zmath.util.mat.rotation" {
9595+ const rotate_origin = zm.matFromRollPitchYaw(0.1, 1.2, 2.3);
9696+ const mat = zm.mul(zm.mul(rotate_origin, zm.scaling(3, 4, 5)), zm.translation(6, 7, 8));
9797+ const rotate_get = getRotationQuat(mat);
9898+ const v0 = zm.mul(zm.f32x4s(1), rotate_origin);
9999+ const v1 = zm.mul(zm.f32x4s(1), zm.quatToMat(rotate_get));
100100+ try expect(zm.approxEqAbs(v0, v1, 0.0001));
101101+}
102102+103103+test "zmath.util.mat.z_vec" {
104104+ const degToRad = std.math.degreesToRadians;
105105+ var identity = zm.identity();
106106+ var z_vec = getAxisZ(identity);
107107+ try expect(zm.approxEqAbs(z_vec, zm.f32x4(0.0, 0.0, 1.0, 0), 0.0001));
108108+ const rot_yaw = zm.rotationY(degToRad(f32, 90));
109109+ identity = zm.mul(identity, rot_yaw);
110110+ z_vec = getAxisZ(identity);
111111+ try expect(zm.approxEqAbs(z_vec, zm.f32x4(1.0, 0.0, 0.0, 0), 0.0001));
112112+}
113113+114114+test "zmath.util.mat.y_vec" {
115115+ const degToRad = std.math.degreesToRadians;
116116+ var identity = zm.identity();
117117+ var y_vec = getAxisY(identity);
118118+ try expect(zm.approxEqAbs(y_vec, zm.f32x4(0.0, 1.0, 0.0, 0), 0.01));
119119+ const rot_yaw = zm.rotationY(degToRad(f32, 90));
120120+ identity = zm.mul(identity, rot_yaw);
121121+ y_vec = getAxisY(identity);
122122+ try expect(zm.approxEqAbs(y_vec, zm.f32x4(0.0, 1.0, 0.0, 0), 0.01));
123123+ const rot_pitch = zm.rotationX(degToRad(f32, 90));
124124+ identity = zm.mul(identity, rot_pitch);
125125+ y_vec = getAxisY(identity);
126126+ try expect(zm.approxEqAbs(y_vec, zm.f32x4(0.0, 0.0, 1.0, 0), 0.01));
127127+}
128128+129129+test "zmath.util.mat.right" {
130130+ const degToRad = std.math.degreesToRadians;
131131+ var identity = zm.identity();
132132+ var right = getAxisX(identity);
133133+ try expect(zm.approxEqAbs(right, zm.f32x4(1.0, 0.0, 0.0, 0), 0.01));
134134+ const rot_yaw = zm.rotationY(degToRad(f32, 90));
135135+ identity = zm.mul(identity, rot_yaw);
136136+ right = getAxisX(identity);
137137+ try expect(zm.approxEqAbs(right, zm.f32x4(0.0, 0.0, -1.0, 0), 0.01));
138138+ const rot_pitch = zm.rotationX(degToRad(f32, 90));
139139+ identity = zm.mul(identity, rot_pitch);
140140+ right = getAxisX(identity);
141141+ try expect(zm.approxEqAbs(right, zm.f32x4(0.0, 1.0, 0.0, 0), 0.01));
142142+}
143143+144144+// ------------------------------------------------------------------------------
145145+// This software is available under 2 licenses -- choose whichever you prefer.
146146+// ------------------------------------------------------------------------------
147147+// ALTERNATIVE A - MIT License
148148+// Copyright (c) 2022 Michal Ziulek and Contributors
149149+// Permission is hereby granted, free of charge, to any person obtaining identity copy of
150150+// this software and associated documentation files (the "Software"), to deal in
151151+// the Software without restriction, including without limitation the rights to
152152+// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
153153+// of the Software, and to permit persons to whom the Software is furnished to do
154154+// so, subject to the following conditions:
155155+// The above copyright notice and this permission notice shall be included in all
156156+// copies or substantial portions of the Software.
157157+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
158158+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
159159+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
160160+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
161161+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
162162+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
163163+// SOFTWARE.
164164+// ------------------------------------------------------------------------------
165165+// ALTERNATIVE B - Public Domain (www.unlicense.org)
166166+// This is free and unencumbered software released into the public domain.
167167+// Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
168168+// software, either in source code form or as identity compiled binary, for any purpose,
169169+// commercial or non-commercial, and by any means.
170170+// In jurisdictions that recognize copyright laws, the author or authors of this
171171+// software dedicate any and all copyright interest in the software to the public
172172+// domain. We make this dedication for the benefit of the public at large and to
173173+// the detriment of our heirs and successors. We intend this dedication to be an
174174+// overt act of relinquishment in perpetuity of all present and future rights to
175175+// this software under copyright law.
176176+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
177177+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
178178+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
179179+// AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
180180+// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
181181+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
182182+// ------------------------------------------------------------------------------