1 /* gph-cmath - C graphics math library
2 * Copyright (C) 2018 John Tsiombikas <nuclear@member.fsf.org>
4 * This program is free software. Feel free to use, modify, and/or redistribute
5 * it under the terms of the MIT/X11 license. See LICENSE for details.
6 * If you intend to redistribute parts of the code without the LICENSE file
7 * replace this paragraph with the full contents of the LICENSE file.
9 static inline void cgm_mcopy(float *dest, const float *src)
11 memcpy(dest, src, 16 * sizeof(float));
14 static inline void cgm_mzero(float *m)
20 static inline void cgm_midentity(float *m)
22 static float id[16] = {1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1};
26 static inline void cgm_mmul(float *a, const float *b)
35 *resptr++ = arow[0] * b[j] + arow[1] * b[4 + j] +
36 arow[2] * b[8 + j] + arow[3] * b[12 + j];
43 static inline void cgm_msubmatrix(float *m, int row, int col)
52 if(i == row) continue;
56 if(j == col) continue;
58 m[subi * 4 + subj++] = orig[i * 4 + j];
66 static inline void cgm_mupper3(float *m)
68 m[3] = m[7] = m[11] = m[12] = m[13] = m[14] = 0.0f;
72 static inline float cgm_msubdet(const float *m, int row, int col)
75 float subdet00, subdet01, subdet02;
78 cgm_msubmatrix(tmp, row, col);
80 subdet00 = tmp[5] * tmp[10] - tmp[6] * tmp[9];
81 subdet01 = tmp[4] * tmp[10] - tmp[6] * tmp[8];
82 subdet02 = tmp[4] * tmp[9] - tmp[5] * tmp[8];
84 return tmp[0] * subdet00 - tmp[1] * subdet01 + tmp[2] * subdet02;
87 static inline float cgm_mcofactor(const float *m, int row, int col)
89 float min = cgm_msubdet(m, row, col);
90 return (row + col) & 1 ? -min : min;
93 static inline float cgm_mdet(const float *m)
95 return m[0] * cgm_msubdet(m, 0, 0) - m[1] * cgm_msubdet(m, 0, 1) +
96 m[2] * cgm_msubdet(m, 0, 2) - m[3] * cgm_msubdet(m, 0, 3);
99 static inline void cgm_mtranspose(float *m)
113 static inline void cgm_mcofmatrix(float *m)
122 m[i * 4 + j] = cgm_mcofactor(tmp, i, j);
127 static inline int cgm_minverse(float *m)
132 float det = cgm_mdet(m);
133 if(det == 0.0f) return -1;
134 inv_det = 1.0f / det;
140 m[i * 4 + j] = cgm_mcofactor(tmp, j, i) * inv_det; /* transposed */
146 static inline void cgm_mtranslation(float *m, float x, float y, float z)
154 static inline void cgm_mscaling(float *m, float sx, float sy, float sz)
163 static inline void cgm_mrotation_x(float *m, float angle)
165 float sa = sin(angle);
166 float ca = cos(angle);
175 static inline void cgm_mrotation_y(float *m, float angle)
177 float sa = sin(angle);
178 float ca = cos(angle);
187 static inline void cgm_mrotation_z(float *m, float angle)
189 float sa = sin(angle);
190 float ca = cos(angle);
199 static inline void cgm_mrotation_axis(float *m, int idx, float angle)
203 cgm_mrotation_x(m, angle);
206 cgm_mrotation_y(m, angle);
209 cgm_mrotation_z(m, angle);
214 static inline void cgm_mrotation(float *m, float angle, float x, float y, float z)
216 float sa = sin(angle);
217 float ca = cos(angle);
218 float invca = 1.0f - ca;
226 m[0] = xsq + (1.0f - xsq) * ca;
227 m[4] = x * y * invca - z * sa;
228 m[8] = x * z * invca + y * sa;
230 m[1] = x * y * invca + z * sa;
231 m[5] = ysq + (1.0f - ysq) * ca;
232 m[9] = y * z * invca - x * sa;
234 m[2] = x * z * invca - y * sa;
235 m[6] = y * z * invca + x * sa;
236 m[10] = zsq + (1.0f - zsq) * ca;
239 static inline void cgm_mrotation_euler(float *m, float a, float b, float c, int mode)
241 /* this array must match the EulerMode enum */
242 static const int axis[][3] = {
243 {0, 1, 2}, {0, 2, 1},
244 {1, 0, 2}, {1, 2, 0},
245 {2, 0, 1}, {2, 1, 0},
246 {2, 0, 2}, {2, 1, 2},
247 {1, 0, 1}, {1, 2, 1},
251 float ma[16], mb[16];
252 cgm_mrotation_axis(ma, axis[mode][0], a);
253 cgm_mrotation_axis(mb, axis[mode][1], b);
254 cgm_mrotation_axis(m, axis[mode][2], c);
259 static inline void cgm_mtranslate(float *m, float x, float y, float z)
262 cgm_mtranslation(tm, x, y, z);
266 static inline void cgm_mscale(float *m, float sx, float sy, float sz)
269 cgm_mscaling(sm, sx, sy, sz);
273 static inline void cgm_mrotate_x(float *m, float angle)
276 cgm_mrotation_x(rm, angle);
280 static inline void cgm_mrotate_y(float *m, float angle)
283 cgm_mrotation_y(rm, angle);
287 static inline void cgm_mrotate_z(float *m, float angle)
290 cgm_mrotation_z(rm, angle);
294 static inline void cgm_mrotate_axis(float *m, int idx, float angle)
297 cgm_mrotation_axis(rm, idx, angle);
301 static inline void cgm_mrotate(float *m, float angle, float x, float y, float z)
304 cgm_mrotation(rm, angle, x, y, z);
308 static inline void cgm_mrotate_euler(float *m, float a, float b, float c, int mode)
311 cgm_mrotation_euler(rm, a, b, c, mode);
316 static inline void cgm_mpretranslate(float *m, float x, float y, float z)
320 cgm_mtranslation(m, x, y, z);
324 static inline void cgm_mprescale(float *m, float sx, float sy, float sz)
328 cgm_mscaling(m, sx, sy, sz);
332 static inline void cgm_mprerotate_x(float *m, float angle)
336 cgm_mrotation_x(m, angle);
340 static inline void cgm_mprerotate_y(float *m, float angle)
344 cgm_mrotation_y(m, angle);
348 static inline void cgm_mprerotate_z(float *m, float angle)
352 cgm_mrotation_z(m, angle);
356 static inline void cgm_mprerotate_axis(float *m, int idx, float angle)
360 cgm_mrotation_axis(m, idx, angle);
364 static inline void cgm_mprerotate(float *m, float angle, float x, float y, float z)
368 cgm_mrotation(m, angle, x, y, z);
372 static inline void cgm_mprerotate_euler(float *m, float a, float b, float c, int mode)
376 cgm_mrotation_euler(m, a, b, c, mode);
381 static inline void cgm_mget_translation(const float *m, cgm_vec3 *res)
388 /* Algorithm in Ken Shoemake's article in 1987 SIGGRAPH course notes
389 * article "Quaternion Calculus and Fast Animation".
390 * adapted from: http://www.geometrictools.com/LibMathematics/Algebra/Wm5Quaternion.inl
392 static inline void cgm_mget_rotation(const float *m, cgm_quat *res)
394 static const int next[3] = {1, 2, 0};
398 float trace = m[0] + m[5] + m[10];
403 root = sqrt(trace + 1.0f); // 2w
404 res->w = 0.5f * root;
405 root = 0.5f / root; // 1 / 4w
406 res->x = (m[6] - m[9]) * root;
407 res->y = (m[8] - m[2]) * root;
408 res->z = (m[1] - m[4]) * root;
415 if(m[10] > m[i * 4 + i]) {
421 root = sqrt(m[i * 4 + i] - m[j * 4 + j] - m[k * 4 + k] + 1.0f);
422 quat[i + 1] = 0.5f * root;
424 quat[0] = (m[j + 4 + k] - m[k * 4 + j]) * root;
425 quat[j + 1] = (m[i * 4 + j] - m[j * 4 + i]) * root;
426 quat[k + 1] = (m[i * 4 + k] - m[k * 4 + i]) * root;
434 static inline void cgm_mget_scaling(const float *m, cgm_vec3 *res)
436 res->x = sqrt(m[0] * m[0] + m[4] * m[4] + m[8] * m[8]);
437 res->y = sqrt(m[1] * m[1] + m[5] * m[5] + m[9] * m[9]);
438 res->z = sqrt(m[2] * m[2] + m[6] * m[6] + m[10] * m[10]);
441 static inline void cgm_mget_frustum_plane(const float *m, int p, cgm_vec4 *res)
444 const float *rowptr = m + row * 4;
447 res->x = m[12] + rowptr[0];
448 res->y = m[13] + rowptr[1];
449 res->z = m[14] + rowptr[2];
450 res->w = m[15] + rowptr[3];
452 res->x = m[12] - rowptr[0];
453 res->y = m[13] - rowptr[1];
454 res->z = m[14] - rowptr[2];
455 res->w = m[15] - rowptr[3];
459 static inline void cgm_mlookat(float *m, const cgm_vec3 *pos, const cgm_vec3 *targ,
463 cgm_vec3 dir = *targ, right, vup;
466 cgm_vnormalize(&dir);
467 cgm_vcross(&right, &dir, up);
468 cgm_vnormalize(&right);
469 cgm_vcross(&vup, &right, &dir);
470 cgm_vnormalize(&vup);
483 cgm_mtranslation(trans, pos->x, pos->y, pos->z);
487 static inline void cgm_minv_lookat(float *m, const cgm_vec3 *pos, const cgm_vec3 *targ,
491 cgm_vec3 dir = *targ, right, vup;
494 cgm_vnormalize(&dir);
495 cgm_vcross(&right, &dir, up);
496 cgm_vnormalize(&right);
497 cgm_vcross(&vup, &right, &dir);
498 cgm_vnormalize(&vup);
511 cgm_mtranslation(m, -pos->x, -pos->y, -pos->z);
515 static inline void cgm_mortho(float *m, float left, float right, float bot, float top,
516 float znear, float zfar)
518 float dx = right - left;
519 float dy = top - bot;
520 float dz = zfar - znear;
526 m[12] = -(right + left) / dx;
527 m[13] = -(top + bot) / dy;
528 m[14] = -(zfar + znear) / dz;
531 static inline void cgm_mfrustum(float *m, float left, float right, float bot, float top,
532 float znear, float zfar)
534 float dx = right - left;
535 float dy = top - bot;
536 float dz = zfar - znear;
539 m[0] = 2.0f * znear / dx;
540 m[5] = 2.0f * znear / dy;
541 m[8] = (right + left) / dx;
542 m[9] = (top + bot) / dy;
543 m[10] = -(zfar + znear) / dz;
544 m[14] = -2.0f * zfar * znear / dz;
548 static inline void cgm_mperspective(float *m, float vfov, float aspect, float znear, float zfar)
550 float s = 1.0f / (float)tan(vfov / 2.0f);
551 float range = znear - zfar;
556 m[10] = (znear + zfar) / range;
557 m[14] = 2.0f * znear * zfar / range;
561 static inline void cgm_mmirror(float *m, float a, float b, float c, float d)
563 m[0] = 1.0f - 2.0f * a * a;
564 m[5] = 1.0f - 2.0f * b * b;
565 m[10] = 1.0f - 2.0f * c * c;
568 m[1] = m[4] = -2.0f * a * b;
569 m[2] = m[8] = -2.0f * a * c;
570 m[6] = m[9] = -2.0f * b * c;
572 m[12] = -2.0f * a * d;
573 m[13] = -2.0f * b * d;
574 m[14] = -2.0f * c * d;
576 m[3] = m[7] = m[11] = 0.0f;