Created
January 8, 2026 07:08
-
-
Save namandixit/4d58119852b37a6fd3183023bd9e6632 to your computer and use it in GitHub Desktop.
Math library for 3D Graphics (tested with Vulkan)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* | |
| * Creator: Naman Dixit | |
| * Notice: © Copyright 2025 Naman Dixit | |
| */ | |
| #pragma once | |
| #include "std.h" | |
| pragma_clang("clang diagnostic push"); | |
| pragma_clang("clang diagnostic ignored \"-Wgnu-anonymous-struct\""); | |
| pragma_msvc("warning ( push )"); | |
| pragma_msvc("warning ( disable: 4201 )"); // nonstandard extension used: nameless struct/union | |
| #if defined(M3D_USE_MATH_H) | |
| # include <math.h> | |
| header_function Float32 f32Sqrt (Float32 f) { return sqrtf(f); } | |
| header_function Float32 f32Abs (Float32 f) { return fabsf(f); } | |
| header_function Float32 f32Max (Float32 f, Float32 g) { return fmaxf(f, g); } | |
| header_function Float32 f32Min (Float32 f, Float32 g) { return fminf(f, g); } | |
| header_function Float32 f32Frac (Float32 f) { Float32 i [[maybe_unused]]; return modff(f, &i); } | |
| header_function Float32 f32Int (Float32 f) { Float32 i; (void)modff(f, &i); return i; } | |
| header_function Float32 f32Sin (Float32 f) { return sinf(f); } | |
| header_function Float32 f32Cos (Float32 f) { return cosf(f); } | |
| header_function Float32 f32Tan (Float32 f) { return tanf(f); } | |
| header_function Float32 f32Atan2 (Float32 y, Float32 x) { return atan2f(y, x); } | |
| #endif | |
| /* Angles */ | |
| // TODO(naman): C23 adds functions like sinpi, cospi, etc. which can be used to calculate transcedental functions in turns. | |
| // Switch to using them once they are supported across OSes and toolchains. | |
| typedef struct Turn { Float32 angle; } Turn; | |
| #define M3D_PI 3.14159265359f | |
| #define M3D_DEG180 180.0f | |
| #define M3D_TURNHALF 0.5f | |
| header_function Turn turn (Float32 x) { Turn t = { .angle = x }; return t; } | |
| header_function Turn turnFromRadian (Float32 rad) { return turn(rad * M3D_TURNHALF / M3D_PI); } | |
| header_function Turn turnFromDegree (Float32 deg) { return turn(deg * M3D_TURNHALF / M3D_DEG180); } | |
| header_function Float32 turnToRadian (Turn turn) { return turn.angle * M3D_PI / M3D_TURNHALF; } | |
| header_function Float32 turnToDegree (Turn turn) { return turn.angle * M3D_DEG180 / M3D_TURNHALF; } | |
| /* 2-D Vectors */ | |
| typedef union { | |
| Float32x2 data; | |
| struct { Float32 x, y; }; | |
| struct { Float32 i, j; }; | |
| struct { Float32 u, v; }; | |
| struct { Float32 width, height; }; | |
| struct { Float32 left, right; }; | |
| struct { Float32 top, bottom; }; | |
| } Vec2f; | |
| static_assert(sizeof(Vec2f) == 8, "Vec2f size is invalid"); | |
| header_function Vec2f vec2f (Float32 x, Float32 y) { Vec2f result = { .x = x, .y = y }; return result; } | |
| header_function Vec2f vec2fZero (void) { return vec2f(0.0f, 0.0f); } | |
| header_function Vec2f vec2fOne (void) { return vec2f(1.0f, 1.0f); } | |
| header_function Vec2f vec2fNegate (Vec2f v) { return vec2f(-v.x, -v.y); } | |
| header_function Vec2f vec2fMirror (Vec2f v) { return vec2f( v.y, v.x); } | |
| header_function Vec2f vec2fAbs (Vec2f v) { return vec2f(f32Abs(v.x), f32Abs(v.y)); } | |
| header_function Float32 vec2fMax (Vec2f v) { return f32Max(v.x, v.y); } | |
| header_function Float32 vec2fMin (Vec2f v) { return f32Min(v.x, v.y); } | |
| header_function Vec2f vec2fAdd (Vec2f v, Vec2f w) { return vec2f(v.x + w.x, v.y + w.y); } | |
| header_function Vec2f vec2fSub (Vec2f v, Vec2f w) { return vec2f(v.x - w.x, v.y - w.y); } | |
| header_function Vec2f vec2fOffsetBy (Vec2f v, Float32 o) { return vec2f(v.x + o, v.y + o); } | |
| header_function Vec2f vec2fMul (Vec2f v, Vec2f w) { return vec2f(v.x * w.x, v.y * w.y); } | |
| header_function Vec2f vec2fDiv (Vec2f v, Vec2f w) { return vec2f(v.x / w.x, v.y / w.y); } | |
| header_function Vec2f vec2fScaleBy (Vec2f v, Float32 s) { return vec2f(v.x * s, v.y * s); } | |
| header_function Float32 vec2fDot (Vec2f v, Vec2f w) { return ((v.x * w.x) + (v.y * w.y)); } | |
| header_function Float32 vec2fCross (Vec2f v, Vec2f w) { return ((v.x * w.y) - (v.y * w.x)); } // https://allenchou.net/2013/07/cross-product-of-2d-vectors/ | |
| header_function Float32 vec2fLenSq (Vec2f v) { return vec2fDot(v, v); } | |
| header_function Float32 vec2fLen (Vec2f v) { return f32Sqrt(vec2fLenSq(v)); } | |
| header_function Vec2f vec2fNorm (Vec2f v) { return vec2fScaleBy(v, 1.0f / vec2fLen(v)); } | |
| header_function Vec2f vec2NormSafe (Vec2f v) | |
| { | |
| Vec2f abs = vec2fAbs(v); | |
| Float32 max = vec2fMax(abs); | |
| if (max == 0.0f) return vec2fZero(); | |
| Vec2f scaled = vec2fScaleBy(v, 1.0f / max); | |
| Float32 len = vec2fLen(scaled); | |
| if (len == 0.0f) return vec2fZero(); | |
| return vec2fScaleBy(scaled, 1.0f / len); | |
| } | |
| header_function Vec2f vec2fScaleToLength (Vec2f v, Float32 l) { return vec2fScaleBy(v, l / vec2fLen(v)); } | |
| header_function Vec2f vec2fLerp (Vec2f v, Vec2f w, Float32 s) { return vec2f(v.x + s * (w.x - v.x), v.y + s * (w.y - v.y)); } | |
| header_function Vec2f vec2fMidPoint (Vec2f v, Vec2f w) { return vec2fScaleBy(vec2fAdd(v, w), 0.5f); } | |
| header_function Float32 vec2fDistanceSq (Vec2f v, Vec2f w) { return (w.x-v.x)*(w.x-v.x) + (w.y-v.y)*(w.y-v.y); } | |
| header_function Float32 vec2fDistance (Vec2f v, Vec2f w) { return f32Sqrt(vec2fDistanceSq(v, w)); } | |
| header_function Vec2f vec2fClamp (Vec2f v, Float32 min, Float32 max) { return vec2f(f32Min(f32Max(v.x, min), max), | |
| f32Min(f32Max(v.y, min), max)); } | |
| header_function Vec2f vec2fProjectOn (Vec2f v, Vec2f w) { return vec2fScaleBy(w, vec2fDot(v, w) / vec2fLenSq(w)); } | |
| /* 3-D Vector */ | |
| typedef union { | |
| Float32x3 data; | |
| struct { Float32 x, y, z; }; | |
| struct { Float32 i, j, k; }; | |
| struct { Float32 u, v, w; }; | |
| struct { Float32 _1; Vec2f yz; }; | |
| struct { Vec2f xy; Float32 _2; }; | |
| struct { Float32 _3; Vec2f vw; }; | |
| struct { Vec2f uv; Float32 _4; }; | |
| struct { Float32 _5; Vec2f jk; }; | |
| struct { Vec2f ij; Float32 _6; }; | |
| struct { Float32 width, height, depth; }; | |
| } Vec3f; | |
| static_assert(sizeof(Vec3f) == 12, "Vec3f size is invalid"); | |
| header_function Vec3f vec3f (Float32 x, Float32 y, Float32 z) { Vec3f result = { .x = x, .y = y, .z = z }; return result; } | |
| header_function Vec3f vec3fZero (void) { return vec3f(0.0f, 0.0f, 0.0f); } | |
| header_function Vec3f vec3fOne (void) { return vec3f(1.0f, 1.0f, 1.0f); } | |
| header_function Vec3f vec3fNegate (Vec3f v) { return vec3f(-v.x, -v.y, -v.z); } | |
| header_function Vec3f vec3fMirror (Vec3f v) { return vec3f( v.z, v.y, v.x); } | |
| header_function Vec3f vec3fShiftLeft (Vec3f v) { return vec3f( v.y, v.z, v.x); } | |
| header_function Vec3f vec3fShiftRight (Vec3f v) { return vec3f( v.z, v.x, v.y); } | |
| header_function Vec3f vec3fAbs (Vec3f v) { return vec3f(f32Abs(v.x), f32Abs(v.y), f32Abs(v.z)); } | |
| header_function Float32 vec3fMax (Vec3f v) { return f32Max(v.x, f32Max(v.y, v.z)); } | |
| header_function Float32 vec3fMin (Vec3f v) { return f32Min(v.x, f32Min(v.y, v.z)); } | |
| header_function Vec3f vec3fAdd (Vec3f v, Vec3f w) { return vec3f(v.x + w.x, v.y + w.y, v.z + w.z); } | |
| header_function Vec3f vec3fSub (Vec3f v, Vec3f w) { return vec3f(v.x - w.x, v.y - w.y, v.z - w.z); } | |
| header_function Vec3f vec3fOffset (Vec3f v, Float32 o) { return vec3f(v.x + o, v.y + o, v.z + o); } | |
| header_function Vec3f vec3fMul (Vec3f v, Vec3f w) { return vec3f(v.x * w.x, v.y * w.y, v.z * w.z); } | |
| header_function Vec3f vec3fDiv (Vec3f v, Vec3f w) { return vec3f(v.x / w.x, v.y / w.y, v.z / w.z); } | |
| header_function Vec3f vec3fScaleBy (Vec3f v, Float32 s) { return vec3f(v.x * s, v.y * s, v.z * s); } | |
| header_function Float32 vec3fDot (Vec3f v, Vec3f w) { return ((v.x * w.x) + (v.y * w.y) + (v.z * w.z)); } | |
| header_function Vec3f vec3fCross (Vec3f v, Vec3f w) { return vec3f((v.y * w.z) - (v.z * w.y), | |
| (v.z * w.x) - (v.x * w.z), | |
| (v.x * w.y) - (v.y * w.x)); } | |
| header_function Float32 vec3fLenSq (Vec3f v) { return vec3fDot(v, v); } | |
| header_function Float32 vec3fLen (Vec3f v) { return f32Sqrt(vec3fLenSq(v)); } | |
| header_function Vec3f vec3fNorm (Vec3f v) { return vec3fScaleBy(v, 1.0f / vec3fLen(v)); } | |
| header_function Vec3f vec3fNormSafe (Vec3f v) | |
| { | |
| Vec3f abs = vec3fAbs(v); | |
| Float32 max = vec3fMax(abs); | |
| if (max == 0.0f) return vec3fZero(); | |
| Vec3f scaled = vec3fScaleBy(v, 1.0f / max); | |
| Float32 len = vec3fLen(scaled); | |
| if (len == 0.0f) return vec3fZero(); | |
| return vec3fScaleBy(scaled, 1.0f / len); | |
| } | |
| header_function Vec3f vec3fScaleToLength (Vec3f v, Float32 l) { return vec3fScaleBy(v, l / vec3fLen(v)); } | |
| header_function Vec3f vec3fLerp (Vec3f v, Vec3f w, Float32 s) { return vec3f(v.x + s * (w.x - v.x), | |
| v.y + s * (w.y - v.y), | |
| v.z + s * (w.z - v.z)); } | |
| header_function Vec3f vec3fMidPoint (Vec3f v, Vec3f w) { return vec3fScaleBy(vec3fAdd(v, w), 0.5f); } | |
| header_function Float32 vec3fDistanceSq (Vec3f v, Vec3f w) { return (w.x-v.x)*(w.x-v.x) + (w.y-v.y)*(w.y-v.y) + (w.z-v.z)*(w.z-v.z); } | |
| header_function Float32 vec3fDistance (Vec3f v, Vec3f w) { return f32Sqrt(vec3fDistanceSq(v, w)); } | |
| header_function Vec3f vec3fClamp (Vec3f v, Float32 min, Float32 max) { return vec3f(f32Min(f32Max(v.x, min), max), | |
| f32Min(f32Max(v.y, min), max), | |
| f32Min(f32Max(v.z, min), max)); } | |
| header_function Vec3f vec3fProjectOn (Vec3f v, Vec3f w) { return vec3fScaleBy(w, vec3fDot(v, w) / vec3fLenSq(w)); } | |
| // https://web.archive.org/web/20160210021457/http://lolengine.net/blog/2013/09/21/picking-orthogonal-vector-combing-coconuts | |
| header_function Vec3f vec3fOrthogonal (Vec3f v) { | |
| Float32 k = f32Frac(f32Abs(v.x) + 0.5f); | |
| return vec3f(-v.y, v.x - k * v.z, k * v.y); | |
| } | |
| /* Rotation Vector */ | |
| // Direction is the axis, the length is the amount of rotation in turns. | |
| typedef union { | |
| Float32x3 data; | |
| Vec3f axis; | |
| struct { Float32 i, j, k; }; | |
| struct { Float32 yz, zx, xy; }; // Plane of rotation for each imaginary component | |
| struct { Float32 x, y, z; }; // Axis of rotation for each imaginary component | |
| } Rot3f; | |
| static_assert(sizeof(Rot3f) == 12, "Rot3f size is invalid"); | |
| header_function Rot3f rot3f (Vec3f axis, Turn angle) { Rot3f r = { .axis = vec3fScaleBy(vec3fNorm(axis), angle.angle), }; return r; } | |
| header_function Vec3f rot3fAxis (Rot3f r) { return vec3fNorm(r.axis); } | |
| header_function Turn rot3fAngle (Rot3f r) { return turn(vec3fLen(r.axis)); } | |
| /* Quaternion */ | |
| typedef union { | |
| Float32x4 data; | |
| struct { Vec3f imag; Float32 real; }; | |
| struct { Float32 i, j, k, r; }; // `i`, `j`, and `k` are imaginary parts, `r` is the real part, | |
| struct { Float32 yz, zx, xy; }; // Plane of rotation for each imaginary component | |
| struct { Float32 x, y, z, w; }; // Axis of rotation for each imaginary component | |
| } Quat4f; | |
| static_assert(sizeof(Quat4f) == 16, "Quat4f size is invalid"); | |
| // WARN(naman): Note that while | |
| header_function Quat4f quat4f (Vec3f imag, Float32 real) { Quat4f q = { .imag = imag, .real = real }; return q; } | |
| header_function Quat4f quat4fFromRot (Rot3f rot) { | |
| Float32 rads = turnToRadian(rot3fAngle(rot)) * 0.5f; | |
| return quat4f(vec3fScaleBy(rot3fAxis(rot), f32Sin(rads)), f32Cos(rads)); | |
| } | |
| header_function Quat4f quat4fOne (void) { return quat4f(vec3fZero(), 1.0f); } | |
| header_function Quat4f quat4fNorm (Quat4f q); | |
| // NOTE(naman): Use of FLT_EPSILON is appropriate in these functions since it is (only) well defined between 1.0 and 2.0. | |
| // Calculates quaternion rotating vector V to W | |
| // V and W should be unit vectors | |
| header_function Quat4f quat4fVecs (Vec3f vunit, Vec3f wunit) { | |
| Float32 cos_theta = vec3fDot(vunit, wunit); | |
| if (cos_theta >= 1.0f - FLT_EPSILON) { // cos() is close to 1 => Vectors are parallel | |
| return quat4fOne(); | |
| } else if (cos_theta < -1.0f + FLT_EPSILON) { // cos() is close to -1 => Vectors are anti parallel | |
| return quat4fNorm(quat4f(vec3fOrthogonal(vunit), 0.0f)); | |
| } else { | |
| return quat4fNorm(quat4f(vec3fCross(vunit, wunit), 1.0f + cos_theta)); | |
| } | |
| } | |
| // Rotation q2, then rotation q1 | |
| header_function | |
| Quat4f quat4fMul (Quat4f q1, Quat4f q2) | |
| { | |
| Vec3f i1 = vec3fScaleBy(q2.imag, q1.real); | |
| Vec3f i2 = vec3fScaleBy(q1.imag, q2.real); | |
| Vec3f i3 = vec3fCross (q1.imag, q2.imag); | |
| Vec3f i = vec3fAdd(i1, vec3fAdd(i2, i3)); | |
| Float32 r = (q1.real * q2.real) - vec3fDot(q1.imag, q2.imag); | |
| return quat4f(i, r); | |
| } | |
| header_function Float32 quat4fMagSq (Quat4f q) { return vec3fLenSq(q.imag) + (q.r * q.r); } | |
| header_function Float32 quat4fMag (Quat4f q) { return f32Sqrt(quat4fMagSq(q)); } | |
| header_function Quat4f quat4fNorm (Quat4f q) { | |
| Float32 inv_mag = 1.0f / quat4fMag(q); | |
| return quat4f(vec3fScaleBy(q.imag, inv_mag), q.real * inv_mag); | |
| } | |
| // WARN(naman): Only works when the quaternion's magnitude is very close to 1. | |
| header_function Quat4f quat4fNormFast (Quat4f normalized) { | |
| Float32 mag = quat4fMagSq(normalized); | |
| Float32 inv_mag; { // Approx 1/sqrt(mag) around 1, from https://allenchou.net/2014/02/game-math-fast-re-normalization-of-unit-vectors/ | |
| const Float32 a0 = 15.0f / 8.0f; | |
| const Float32 a1 = -5.0f / 4.0f; | |
| const Float32 a2 = 3.0f / 8.0f; | |
| inv_mag = a0 + (a1 * mag) + (a2 * mag * mag); | |
| } | |
| return quat4f(vec3fScaleBy(normalized.imag, inv_mag), normalized.real * inv_mag); | |
| } | |
| // THese function reverse the rotation of a quaternion | |
| header_function Quat4f quat4fConjugate (Quat4f q) { return quat4f(vec3fNegate(q.imag), q.real); } // Fast inverse for normalized quats | |
| header_function Quat4f quat4fInverse (Quat4f q) { // q ^ -1 | |
| Quat4f qc = quat4fConjugate(q); | |
| Float32 iqms = 1.0f / quat4fMagSq(q); | |
| return quat4f(vec3fScaleBy(qc.imag, iqms), qc.real * iqms); | |
| } | |
| header_function Float32 quat4fAngle (Quat4f q) { | |
| // sin(theta / 2) = length(x*x + y*y + z*z) | |
| // cos(theta / 2) = w | |
| // theta = 2 * atan(sin(theta / 2) / cos(theta / 2)) | |
| return 2.0f * f32Atan2(vec3fLen(q.imag), q.real); | |
| } | |
| header_function Vec3f quat4fAxis (Quat4f q) { return quat4fNorm(q).imag; } | |
| // q * v * q^-1 | |
| // Copied from cglm headers | |
| header_function Vec3f quat4fRotateVector (Quat4f q, Vec3f v) { | |
| Quat4f p = quat4fNorm(q); | |
| Vec3f u = p.imag; | |
| Float32 s = p.real; | |
| Vec3f v1 = vec3fScaleBy(u, 2.0f * vec3fDot(u, v)); | |
| Vec3f v2 = vec3fScaleBy(v, s * s - vec3fDot(u, u)); | |
| v1 = vec3fAdd(v1, v2); | |
| v2 = vec3fCross(u, v); | |
| v2 = vec3fScaleBy(v2, 2.0f * s); | |
| return vec3fAdd(v1, v2); | |
| } | |
| /* Matrix */ | |
| typedef union Mat4f { | |
| /* Column-major: | |
| _ _ | |
| | 0 4 8 12 | | |
| | 1 5 9 13 | | |
| | 2 6 10 14 | | |
| | 3 7 11 15 | | |
| - - | |
| */ | |
| Float32 elem[16]; | |
| struct { | |
| /* 0 1 2 3 */ | |
| Float32 a00, a10, a20, a30; | |
| /* 4 5 6 7 */ | |
| Float32 a01, a11, a21, a31; | |
| /* 8 9 10 11 */ | |
| Float32 a02, a12, a22, a32; | |
| /* 12 13 14 15 */ | |
| Float32 a03, a13, a23, a33; | |
| }; | |
| struct { | |
| Float32x4 c0; | |
| Float32x4 c1; | |
| Float32x4 c2; | |
| Float32x4 c3; | |
| } columns; | |
| Float32x4 arrays[4]; | |
| } Mat4f; | |
| // NOTE(naman): All matrix functions operate on column-major matrices with a | |
| // right-handed coordinate system. | |
| header_function | |
| Mat4f mat4fZero (void) | |
| { | |
| Mat4f m = {}; | |
| return m; | |
| } | |
| header_function | |
| Mat4f mat4fOne (void) | |
| { | |
| Mat4f m = mat4fZero(); | |
| m.elem[0] = 1; | |
| m.elem[5] = 1; | |
| m.elem[10] = 1; | |
| m.elem[15] = 1; | |
| return m; | |
| } | |
| header_function | |
| Mat4f mat4fAdd (Mat4f m, Mat4f n) | |
| { | |
| Mat4f o = mat4fZero(); | |
| for (Uint i = 0; i < 16; ++i) { | |
| o.elem[i] = m.elem[i] + n.elem[i]; | |
| } | |
| return o; | |
| } | |
| header_function | |
| Mat4f mat4fSub (Mat4f m, Mat4f n) | |
| { | |
| Mat4f o = mat4fZero(); | |
| for (Uint i = 0; i < 16; ++i) { | |
| o.elem[i] = m.elem[i] - n.elem[i]; | |
| } | |
| return o; | |
| } | |
| header_function | |
| Mat4f mat4fMul (Mat4f m, Mat4f n) | |
| { | |
| Mat4f o = mat4fZero(); | |
| for (Uint i = 0; i < 4; i++) { | |
| for (Uint j = 0; j < 4; j++) { | |
| Uint jj = j * 4; | |
| o.elem[i+jj] = ((m.elem[i] * n.elem[jj]) + | |
| (m.elem[i+4] * n.elem[jj+1]) + | |
| (m.elem[i+8] * n.elem[jj+2]) + | |
| (m.elem[i+12] * n.elem[jj+3])); | |
| } | |
| } | |
| return o; | |
| } | |
| header_function | |
| Mat4f mat4fScaleBy (Mat4f m, Float32 r) | |
| { | |
| Mat4f n = mat4fZero(); | |
| for (Uint i = 0; i < 16; ++i) { | |
| n.elem[i] = m.elem[i] * r; | |
| } | |
| return n; | |
| } | |
| header_function | |
| Float32 mat4fDeterminant (Mat4f m) | |
| { | |
| Float32 a = (m.elem[6] * m.elem[11]) - (m.elem[10] * m.elem[7]); | |
| Float32 b = (m.elem[10] * m.elem[15]) - (m.elem[14] * m.elem[11]); | |
| Float32 c = (m.elem[6] * m.elem[15]) - (m.elem[14] * m.elem[7]); | |
| Float32 d = (m.elem[2] * m.elem[11]) - (m.elem[10] * m.elem[3]); | |
| Float32 e = (m.elem[2] * m.elem[15]) - (m.elem[14] * m.elem[3]); | |
| Float32 f = (m.elem[2] * m.elem[7]) - (m.elem[6] * m.elem[3]); | |
| Float32 aa = (m.elem[5] * b) - (m.elem[9] * c) + (m.elem[13] * a); | |
| Float32 bb = (m.elem[1] * b) - (m.elem[9] * e) + (m.elem[13] * d); | |
| Float32 cc = (m.elem[1] * c) - (m.elem[5] * e) + (m.elem[13] * f); | |
| Float32 dd = (m.elem[1] * a) - (m.elem[5] * d) + (m.elem[9] * f); | |
| Float32 det = ((m.elem[0] * aa) - (m.elem[4] * bb) + | |
| (m.elem[8] * cc) - (m.elem[12] * dd)); | |
| return det; | |
| } | |
| header_function | |
| Mat4f mat4fInverse (Mat4f m) | |
| { | |
| Mat4f n = mat4fZero(); | |
| n.elem[0] = ((m.elem[5] * m.elem[10] * m.elem[15]) - | |
| (m.elem[5] * m.elem[11] * m.elem[14]) - | |
| (m.elem[9] * m.elem[6] * m.elem[15]) + | |
| (m.elem[9] * m.elem[7] * m.elem[14]) + | |
| (m.elem[13] * m.elem[6] * m.elem[11]) - | |
| (m.elem[13] * m.elem[7] * m.elem[10])); | |
| n.elem[1] = ((-m.elem[1] * m.elem[10] * m.elem[15]) + | |
| (m.elem[1] * m.elem[11] * m.elem[14]) + | |
| (m.elem[9] * m.elem[2] * m.elem[15]) - | |
| (m.elem[9] * m.elem[3] * m.elem[14]) - | |
| (m.elem[13] * m.elem[2] * m.elem[11]) + | |
| (m.elem[13] * m.elem[3] * m.elem[10])); | |
| n.elem[2] = ((m.elem[1] * m.elem[6] * m.elem[15]) - | |
| (m.elem[1] * m.elem[7] * m.elem[14]) - | |
| (m.elem[5] * m.elem[2] * m.elem[15]) + | |
| (m.elem[5] * m.elem[3] * m.elem[14]) + | |
| (m.elem[13] * m.elem[2] * m.elem[7]) - | |
| (m.elem[13] * m.elem[3] * m.elem[6])); | |
| n.elem[3] = ((-m.elem[1] * m.elem[6] * m.elem[11]) + | |
| (m.elem[1] * m.elem[7] * m.elem[10]) + | |
| (m.elem[5] * m.elem[2] * m.elem[11]) - | |
| (m.elem[5] * m.elem[3] * m.elem[10]) - | |
| (m.elem[9] * m.elem[2] * m.elem[7]) + | |
| (m.elem[9] * m.elem[3] * m.elem[6])); | |
| n.elem[4] = ((-m.elem[4] * m.elem[10] * m.elem[15]) + | |
| (m.elem[4] * m.elem[11] * m.elem[14]) + | |
| (m.elem[8] * m.elem[6] * m.elem[15]) - | |
| (m.elem[8] * m.elem[7] * m.elem[14]) - | |
| (m.elem[12] * m.elem[6] * m.elem[11]) + | |
| (m.elem[12] * m.elem[7] * m.elem[10])); | |
| n.elem[5] = ((m.elem[0] * m.elem[10] * m.elem[15]) - | |
| (m.elem[0] * m.elem[11] * m.elem[14]) - | |
| (m.elem[8] * m.elem[2] * m.elem[15]) + | |
| (m.elem[8] * m.elem[3] * m.elem[14]) + | |
| (m.elem[12] * m.elem[2] * m.elem[11]) - | |
| (m.elem[12] * m.elem[3] * m.elem[10])); | |
| n.elem[6] = ((-m.elem[0] * m.elem[6] * m.elem[15]) + | |
| (m.elem[0] * m.elem[7] * m.elem[14]) + | |
| (m.elem[4] * m.elem[2] * m.elem[15]) - | |
| (m.elem[4] * m.elem[3] * m.elem[14]) - | |
| (m.elem[12] * m.elem[2] * m.elem[7]) + | |
| (m.elem[12] * m.elem[3] * m.elem[6])); | |
| n.elem[7] = ((m.elem[0] * m.elem[6] * m.elem[11]) - | |
| (m.elem[0] * m.elem[7] * m.elem[10]) - | |
| (m.elem[4] * m.elem[2] * m.elem[11]) + | |
| (m.elem[4] * m.elem[3] * m.elem[10]) + | |
| (m.elem[8] * m.elem[2] * m.elem[7]) - | |
| (m.elem[8] * m.elem[3] * m.elem[6])); | |
| n.elem[8] = ((m.elem[4] * m.elem[9] * m.elem[15]) - | |
| (m.elem[4] * m.elem[11] * m.elem[13]) - | |
| (m.elem[8] * m.elem[5] * m.elem[15]) + | |
| (m.elem[8] * m.elem[7] * m.elem[13]) + | |
| (m.elem[12] * m.elem[5] * m.elem[11]) - | |
| (m.elem[12] * m.elem[7] * m.elem[9])); | |
| n.elem[9] = ((-m.elem[0] * m.elem[9] * m.elem[15]) + | |
| (m.elem[0] * m.elem[11] * m.elem[13]) + | |
| (m.elem[8] * m.elem[1] * m.elem[15]) - | |
| (m.elem[8] * m.elem[3] * m.elem[13]) - | |
| (m.elem[12] * m.elem[1] * m.elem[11]) + | |
| (m.elem[12] * m.elem[3] * m.elem[9])); | |
| n.elem[10] = ((m.elem[0] * m.elem[5] * m.elem[15]) - | |
| (m.elem[0] * m.elem[7] * m.elem[13]) - | |
| (m.elem[4] * m.elem[1] * m.elem[15]) + | |
| (m.elem[4] * m.elem[3] * m.elem[13]) + | |
| (m.elem[12] * m.elem[1] * m.elem[7]) - | |
| (m.elem[12] * m.elem[3] * m.elem[5])); | |
| n.elem[11] = ((-m.elem[0] * m.elem[5] * m.elem[11]) + | |
| (m.elem[0] * m.elem[7] * m.elem[9]) + | |
| (m.elem[4] * m.elem[1] * m.elem[11]) - | |
| (m.elem[4] * m.elem[3] * m.elem[9]) - | |
| (m.elem[8] * m.elem[1] * m.elem[7]) + | |
| (m.elem[8] * m.elem[3] * m.elem[5])); | |
| n.elem[12] = ((-m.elem[4] * m.elem[9] * m.elem[14]) + | |
| (m.elem[4] * m.elem[10] * m.elem[13]) + | |
| (m.elem[8] * m.elem[5] * m.elem[14]) - | |
| (m.elem[8] * m.elem[6] * m.elem[13]) - | |
| (m.elem[12] * m.elem[5] * m.elem[10]) + | |
| (m.elem[12] * m.elem[6] * m.elem[9])); | |
| n.elem[13] = ((m.elem[0] * m.elem[9] * m.elem[14]) - | |
| (m.elem[0] * m.elem[10] * m.elem[13]) - | |
| (m.elem[8] * m.elem[1] * m.elem[14]) + | |
| (m.elem[8] * m.elem[2] * m.elem[13]) + | |
| (m.elem[12] * m.elem[1] * m.elem[10]) - | |
| (m.elem[12] * m.elem[2] * m.elem[9])); | |
| n.elem[14] = ((-m.elem[0] * m.elem[5] * m.elem[14]) + | |
| (m.elem[0] * m.elem[6] * m.elem[13]) + | |
| (m.elem[4] * m.elem[1] * m.elem[14]) - | |
| (m.elem[4] * m.elem[2] * m.elem[13]) - | |
| (m.elem[12] * m.elem[1] * m.elem[6]) + | |
| (m.elem[12] * m.elem[2] * m.elem[5])); | |
| n.elem[15] = ((m.elem[0] * m.elem[5] * m.elem[10]) - | |
| (m.elem[0] * m.elem[6] * m.elem[9]) - | |
| (m.elem[4] * m.elem[1] * m.elem[10]) + | |
| (m.elem[4] * m.elem[2] * m.elem[9]) + | |
| (m.elem[8] * m.elem[1] * m.elem[6]) - | |
| (m.elem[8] * m.elem[2] * m.elem[5])); | |
| Float32 det = (m.elem[0] * n.elem[0] + | |
| m.elem[4] * n.elem[1] + | |
| m.elem[8] * n.elem[2] + | |
| m.elem[12] * n.elem[3]); | |
| det = 1.0f / det; | |
| return mat4fScaleBy(n, det); | |
| } | |
| header_function | |
| Mat4f mat4fTranslate2D (Vec2f v) | |
| { | |
| Mat4f m = mat4fOne(); | |
| m.elem[12] = v.x; | |
| m.elem[13] = v.y; | |
| return m; | |
| } | |
| header_function | |
| Mat4f mat4fTranslate3D (Vec3f v) | |
| { | |
| Mat4f m = mat4fOne(); | |
| m.elem[12] = v.x; | |
| m.elem[13] = v.y; | |
| m.elem[14] = v.z; | |
| return m; | |
| } | |
| header_function | |
| Mat4f mat4fRotate3D (Quat4f q) | |
| { | |
| Mat4f m; | |
| m.elem[0] = 1.0f - (2.0f * q.j * q.j) - (2.0f * q.k * q.k); | |
| m.elem[1] = (2.0f * q.i * q.j) + (2.0f * q.k * q.r); | |
| m.elem[2] = (2.0f * q.i * q.k) - (2.0f * q.j * q.r); | |
| m.elem[3] = 0.0f; | |
| m.elem[4] = (2.0f * q.i * q.j) - (2.0f * q.k * q.r); | |
| m.elem[5] = 1.0f - (2.0f * q.i * q.i) - (2.0f * q.k * q.k); | |
| m.elem[6] = (2.0f * q.j * q.k) + (2.0f * q.i * q.r); | |
| m.elem[7] = 0.0f; | |
| m.elem[8] = (2.0f * q.i * q.k) + (2.0f * q.j * q.r); | |
| m.elem[9] = (2.0f * q.j * q.k) - (2.0f * q.i * q.r); | |
| m.elem[10] = 1.0f - (2.0f * q.i * q.i) - (2.0f * q.j * q.j); | |
| m.elem[11] = 0.0f; | |
| m.elem[12] = 0.0f; | |
| m.elem[13] = 0.0f; | |
| m.elem[14] = 0.0f; | |
| m.elem[15] = 1.0f; | |
| return m; | |
| } | |
| header_function | |
| Mat4f mat4fScale2D (Vec2f v) | |
| { | |
| Mat4f m = mat4fZero(); | |
| m.elem[0] = v.x; | |
| m.elem[5] = v.y; | |
| m.elem[10] = 1; | |
| m.elem[15] = 1; | |
| return m; | |
| } | |
| header_function | |
| Mat4f mat4fScale3D (Vec3f v) | |
| { | |
| Mat4f m = mat4fZero(); | |
| m.elem[0] = v.x; | |
| m.elem[5] = v.y; | |
| m.elem[10] = v.z; | |
| m.elem[15] = 1; | |
| return m; | |
| } | |
| header_function | |
| Mat4f mat4fLookAt (Vec3f eye, Vec3f lookat, Vec3f world_up) | |
| { | |
| Vec3f direction = vec3fNorm(vec3fSub(lookat, eye)); | |
| Vec3f right = vec3fNorm(vec3fCross(direction, world_up)); | |
| Vec3f up = vec3fCross(right, direction); | |
| Mat4f m; | |
| m.elem[0] = right.x; | |
| m.elem[1] = up.x; | |
| m.elem[2] = -direction.x; | |
| m.elem[3] = 0.0f; | |
| m.elem[4] = right.y; | |
| m.elem[5] = up.y; | |
| m.elem[6] = -direction.y; | |
| m.elem[7] = 0.0f; | |
| m.elem[8] = right.z; | |
| m.elem[9] = up.z; | |
| m.elem[10] = -direction.z; | |
| m.elem[11] = 0.0f; | |
| m.elem[12] = 0.0f; | |
| m.elem[13] = 0.0f; | |
| m.elem[14] = 0.0f; | |
| m.elem[15] = 1.0f; | |
| Mat4f n = mat4fTranslate3D(vec3fNegate(eye)); | |
| Mat4f result = mat4fMul(m, n); | |
| return result; | |
| } | |
| // Right-handed coordinate system with depth range [0, 1] | |
| header_function | |
| Mat4f mat4fOrthographic (Float32 left, Float32 right, | |
| Float32 bottom, Float32 top, | |
| Float32 near_plane, Float32 far_plane) | |
| { | |
| Mat4f m = mat4fZero(); | |
| Float32 rl = 1.0f / (right - left); | |
| Float32 tb = 1.0f / (top - bottom); | |
| Float32 fn = -1.0f / (far_plane - near_plane); | |
| m.elem[0] = 2.0f * rl; | |
| m.elem[5] = 2.0f * tb; | |
| m.elem[10] = fn; | |
| m.elem[12] = -(right + left) * rl; | |
| m.elem[13] = -(top + bottom) * tb; | |
| m.elem[14] = near_plane * fn; | |
| m.elem[15] = 1.0f; | |
| return m; | |
| } | |
| // Right-handed coordinate system with depth range [0, 1] | |
| header_function | |
| Mat4f mat4fPerspective (Float32 y_fov, Float32 aspect, Float32 near_plane, Float32 far_plane) | |
| { | |
| Mat4f m = mat4fZero(); | |
| Float32 a = 1.0f / f32Tan(y_fov * 0.5f); | |
| m.elem[0] = a / aspect; | |
| m.elem[5] = a; | |
| m.elem[10] = -far_plane / (far_plane - near_plane); | |
| m.elem[11] = -1.0f; | |
| m.elem[14] = -(far_plane * near_plane) / (far_plane - near_plane); | |
| return m; | |
| } | |
| /* Colours */ | |
| typedef union ColRGB { | |
| Float32x3 data; | |
| struct {Float32 r, g, b; }; | |
| struct {Float32 red, green, blue; }; | |
| } ColRGB; | |
| header_function ColRGB colRGB (Float32 r, Float32 g, Float32 b) { ColRGB c = {.r = r, .g = g, .b = b}; return c; } | |
| typedef union ColRGBA { | |
| Float32x4 data; | |
| struct {Float32 r, g, b, a; }; | |
| struct {Float32 red, green, blue, alpha; }; | |
| } ColRGBA; | |
| header_function ColRGBA colRGBA (Float32 r, Float32 g, Float32 b, Float32 a) { ColRGBA c = {.r = r, .g = g, .b = b, .a = a}; return c; } | |
| pragma_msvc("warning ( pop )"); | |
| pragma_clang("clang diagnostic pop"); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment