Skip to content

Instantly share code, notes, and snippets.

@namandixit
Created January 8, 2026 07:08
Show Gist options
  • Select an option

  • Save namandixit/4d58119852b37a6fd3183023bd9e6632 to your computer and use it in GitHub Desktop.

Select an option

Save namandixit/4d58119852b37a6fd3183023bd9e6632 to your computer and use it in GitHub Desktop.
Math library for 3D Graphics (tested with Vulkan)
/*
* 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