LibGame v0.4.0
The LG Game Engine - Copyright (C) 2024-2026 ETMSoftware
Loading...
Searching...
No Matches
math_3d.h
Go to the documentation of this file.
1
101#ifndef MATH_3D_HEADER
102#define MATH_3D_HEADER
103
105// Define PI directly because we would need to define the _BSD_SOURCE or
106// _XOPEN_SOURCE feature test macros to get it from math.h. That would be a
107// rather harsh dependency. So we define it directly if necessary.
108#ifndef M_PI
109#define M_PI 3.14159265358979323846
110#endif
114/*
115 * 3D vectors
116 * Use the `vec3()` function to create vectors. All other vector functions start
117 * with the `v3_` prefix.
118 *
119 * The binary layout is the same as in GLSL and everything else (just 3 floats).
120 * So you can just upload the vectors into shaders as they are.
121*/
122
123typedef struct {float x, y, z; } vec3_t;
124static inline vec3_t vec3(float x, float y, float z) {return (vec3_t){x, y, z };}
125static inline vec3_t v3_add (vec3_t a, vec3_t b) {return (vec3_t){a.x + b.x, a.y + b.y, a.z + b.z };}
126static inline vec3_t v3_adds (vec3_t a, float s) {return (vec3_t){a.x + s, a.y + s, a.z + s };}
127static inline vec3_t v3_sub (vec3_t a, vec3_t b) {return (vec3_t){a.x - b.x, a.y - b.y, a.z - b.z };}
128static inline vec3_t v3_subs (vec3_t a, float s) {return (vec3_t){a.x - s, a.y - s, a.z - s };}
129static inline vec3_t v3_mul (vec3_t a, vec3_t b) {return (vec3_t){a.x * b.x, a.y * b.y, a.z * b.z };}
130static inline vec3_t v3_muls (vec3_t a, float s) {return (vec3_t){a.x * s, a.y * s, a.z * s };}
131static inline vec3_t v3_div (vec3_t a, vec3_t b) {return (vec3_t){a.x / b.x, a.y / b.y, a.z / b.z };}
132static inline vec3_t v3_divs (vec3_t a, float s) {return (vec3_t){a.x / s, a.y / s, a.z / s };}
133static inline float v3_length(vec3_t v) {return sqrtf(v.x * v.x + v.y * v.y + v.z * v.z);}
134static inline vec3_t v3_norm (vec3_t v);
135static inline float v3_dot (vec3_t a, vec3_t b) {return a.x * b.x + a.y * b.y + a.z * b.z;}
136static inline vec3_t v3_proj (vec3_t v, vec3_t onto);
137static inline vec3_t v3_cross (vec3_t a, vec3_t b);
138static inline float v3_angle_between(vec3_t a, vec3_t b);
139
140
141/*
142 * 4x4 matrices
143 *
144 * Use the `mat4()` function to create a matrix. You can write the matrix
145 * members in the same way as you would write them on paper or on a whiteboard:
146 *
147 * mat4_t m = mat4(
148 * 1, 0, 0, 7,
149 * 0, 1, 0, 5,
150 * 0, 0, 1, 3,
151 * 0, 0, 0, 1
152 * )
153 *
154 * This creates a matrix that translates points by vec3(7, 5, 3). All other
155 * matrix functions start with the `m4_` prefix. Among them functions to create
156 * identity, translation, rotation, scaling and projection matrices.
157 *
158 * The matrix is stored in column-major order, just as OpenGL expects. Members
159 * can be accessed by indices or member names. When you write a matrix on paper
160 * or on the whiteboard the indices and named members correspond to these
161 * positions:
162 *
163 * | m[0][0] m[1][0] m[2][0] m[3][0] |
164 * | m[0][1] m[1][1] m[2][1] m[3][1] |
165 * | m[0][2] m[1][2] m[2][2] m[3][2] |
166 * | m[0][3] m[1][3] m[2][3] m[3][3] |
167 *
168 * | m00 m10 m20 m30 |
169 * | m01 m11 m21 m31 |
170 * | m02 m12 m22 m32 |
171 * | m03 m13 m23 m33 |
172 *
173 * The first index or number in a name denotes the column, the second the row.
174 * So m[i][j] denotes the member in the ith column and the jth row. This is the
175 * same as in GLSL (source: GLSL v1.3 specification, 5.6 Matrix Components).
176 */
177
178
179typedef union {
180 // The first index is the column index, the second the row index. The memory
181 // layout of nested arrays in C matches the memory layout expected by OpenGL.
182 float m[4][4];
183 // OpenGL expects the first 4 floats to be the first column of the matrix.
184 // So we need to define the named members column by column for the names to
185 // match the memory locations of the array elements.
186 struct {
187 float m00, m01, m02, m03;
188 float m10, m11, m12, m13;
189 float m20, m21, m22, m23;
190 float m30, m31, m32, m33;
191 };
192} mat4_t;
193
194static inline mat4_t mat4(
195 float m00, float m10, float m20, float m30,
196 float m01, float m11, float m21, float m31,
197 float m02, float m12, float m22, float m32,
198 float m03, float m13, float m23, float m33
199);
200
201static inline mat4_t m4_identity ();
202static inline mat4_t m4_translation (vec3_t offset);
203static inline mat4_t m4_scaling (vec3_t scale);
204static inline mat4_t m4_rotation_x (float angle_in_rad);
205static inline mat4_t m4_rotation_y (float angle_in_rad);
206static inline mat4_t m4_rotation_z (float angle_in_rad);
207 mat4_t m4_rotation (float angle_in_rad, vec3_t axis);
208
209/* Renamed stuff by Emmanuel Thomas-Maurin */
210 mat4_t m4_ortho_RH (float left, float right, float bottom, float top, float back, float front);
211 mat4_t m4_perspective_RH (float vertical_field_of_view_in_deg, float aspect_ratio, float near_view_distance, float far_view_distance);
212 mat4_t m4_look_at_RH (vec3_t from, vec3_t to, vec3_t up);
213/* Not implemented yet:
214 mat4_t m4_ortho_LH (float left, float right, float bottom, float top, float back, float front);
215 mat4_t m4_perspective_LH (float vertical_field_of_view_in_deg, float aspect_ratio, float near_view_distance, float far_view_distance);
216 mat4_t m4_look_at_LH (vec3_t from, vec3_t to, vec3_t up);
217*/
218/* (until here) */
219 mat4_t m4_frustum (float left, float right, float bottom, float top, float near, float far);
220
221static inline mat4_t m4_transpose (mat4_t matrix);
222static inline mat4_t m4_mul (mat4_t a, mat4_t b);
223 mat4_t m4_invert_affine(mat4_t matrix);
224 vec3_t m4_mul_pos (mat4_t matrix, vec3_t position);
225 vec3_t m4_mul_dir (mat4_t matrix, vec3_t direction);
226
227 void m4_print (mat4_t matrix);
228 void m4_printp (mat4_t matrix, int width, int precision);
229 void m4_fprint (FILE* stream, mat4_t matrix);
230 void m4_fprintp (FILE* stream, mat4_t matrix, int width, int precision);
231
232 /* New stuff here */
233 void m4_print2 (mat4_t matrix, const char *line_start);
234 void m4_printp2 (mat4_t matrix, int width, int precision, const char *line_start);
235 void m4_fprint2 (FILE* stream, mat4_t matrix, const char *line_start);
236 void m4_fprintp2 (FILE* stream, mat4_t matrix, int width, int precision, const char *line_start);
237
238
239/*
240 * 3D vector functions header implementation
241 */
242
243static inline vec3_t v3_norm(vec3_t v)
244{
245 float len = v3_length(v);
246 if (len > 0) {
247 return (vec3_t){v.x / len, v.y / len, v.z / len};
248 } else {
249 return (vec3_t){0, 0, 0};
250 }
251}
252
253static inline vec3_t v3_proj(vec3_t v, vec3_t onto)
254{
255 return v3_muls(onto, v3_dot(v, onto) / v3_dot(onto, onto));
256}
257
258static inline vec3_t v3_cross(vec3_t a, vec3_t b)
259{
260 return (vec3_t){
261 a.y * b.z - a.z * b.y,
262 a.z * b.x - a.x * b.z,
263 a.x * b.y - a.y * b.x
264 };
265}
266
267static inline float v3_angle_between(vec3_t a, vec3_t b)
268{
269 return acosf(v3_dot(a, b) / (v3_length(a) * v3_length(b)));
270}
271
272/*
273 * Matrix functions header implementation
274 */
275
276static inline mat4_t mat4(
277 float m00, float m10, float m20, float m30,
278 float m01, float m11, float m21, float m31,
279 float m02, float m12, float m22, float m32,
280 float m03, float m13, float m23, float m33
281 )
282{
283 return (mat4_t){
284 .m[0][0] = m00, .m[1][0] = m10, .m[2][0] = m20, .m[3][0] = m30,
285 .m[0][1] = m01, .m[1][1] = m11, .m[2][1] = m21, .m[3][1] = m31,
286 .m[0][2] = m02, .m[1][2] = m12, .m[2][2] = m22, .m[3][2] = m32,
287 .m[0][3] = m03, .m[1][3] = m13, .m[2][3] = m23, .m[3][3] = m33
288 };
289}
290
291static inline mat4_t m4_identity()
292{
293 return mat4(
294 1, 0, 0, 0,
295 0, 1, 0, 0,
296 0, 0, 1, 0,
297 0, 0, 0, 1
298 );
299}
300
301static inline mat4_t m4_translation(vec3_t offset)
302{
303 return mat4(
304 1, 0, 0, offset.x,
305 0, 1, 0, offset.y,
306 0, 0, 1, offset.z,
307 0, 0, 0, 1
308 );
309}
310
311static inline mat4_t m4_scaling(vec3_t scale)
312{
313 float x = scale.x, y = scale.y, z = scale.z;
314 return mat4(
315 x, 0, 0, 0,
316 0, y, 0, 0,
317 0, 0, z, 0,
318 0, 0, 0, 1
319 );
320}
321
322static inline mat4_t m4_rotation_x(float angle_in_rad)
323{
324 float s = sinf(angle_in_rad), c = cosf(angle_in_rad);
325 return mat4(
326 1, 0, 0, 0,
327 0, c, -s, 0,
328 0, s, c, 0,
329 0, 0, 0, 1
330 );
331}
332
333static inline mat4_t m4_rotation_y(float angle_in_rad)
334{
335 float s = sinf(angle_in_rad), c = cosf(angle_in_rad);
336 return mat4(
337 c, 0, s, 0,
338 0, 1, 0, 0,
339 -s, 0, c, 0,
340 0, 0, 0, 1
341 );
342}
343
344static inline mat4_t m4_rotation_z(float angle_in_rad)
345{
346 float s = sinf(angle_in_rad), c = cosf(angle_in_rad);
347 return mat4(
348 c, -s, 0, 0,
349 s, c, 0, 0,
350 0, 0, 1, 0,
351 0, 0, 0, 1
352 );
353}
354
355static inline mat4_t m4_transpose(mat4_t matrix)
356{
357 return mat4(
358 matrix.m00, matrix.m01, matrix.m02, matrix.m03,
359 matrix.m10, matrix.m11, matrix.m12, matrix.m13,
360 matrix.m20, matrix.m21, matrix.m22, matrix.m23,
361 matrix.m30, matrix.m31, matrix.m32, matrix.m33
362 );
363}
364
365/*
366 * Multiplication of two 4x4 matrices.
367 *
368 * Implemented by following the row times column rule and illustrating it on a
369 * whiteboard with the proper indices in mind.
370 *
371 * Further reading: https://en.wikipedia.org/wiki/Matrix_multiplication
372 * But note that the article use the first index for rows and the second for
373 * columns.
374 */
375static inline mat4_t m4_mul(mat4_t a, mat4_t b)
376{
377 mat4_t result;
378 int i, j, k;
379
380 for (i = 0; i < 4; i++) {
381 for (j = 0; j < 4; j++) {
382 float sum = 0;
383 for (k = 0; k < 4; k++) {
384 sum += a.m[k][j] * b.m[i][k];
385 }
386 result.m[i][j] = sum;
387 }
388 }
389
390 return result;
391}
392
393#endif // MATH_3D_HEADER
394
395
396#ifdef MATH_3D_IMPLEMENTATION
397
398// Can't get that to show in doc
399
412mat4_t m4_rotation(float angle_in_rad, vec3_t axis)
413{
414 vec3_t normalized_axis = v3_norm(axis);
415 float x = normalized_axis.x, y = normalized_axis.y, z = normalized_axis.z;
416 float c = cosf(angle_in_rad), s = sinf(angle_in_rad);
417
418 return mat4(
419 c + x * x * (1 - c), x * y * (1 - c) - z * s, x * z * (1 - c) + y * s, 0,
420 y * x * (1 - c) + z * s, c + y * y * (1 - c), y * z * (1 - c) - x * s, 0,
421 z * x * (1 - c) - y * s, z * y * (1 - c) + x * s, c + z * z * (1 - c), 0,
422 0, 0, 0, 1
423 );
424}
425
453mat4_t m4_ortho_RH(float left, float right, float bottom, float top, float back, float front)
454{
455 float l = left, r = right, b = bottom, t = top, n = front, f = back;
456 float tx = -(r + l) / (r - l);
457 float ty = -(t + b) / (t - b);
458 float tz = -(f + n) / (f - n);
459
460 return mat4(
461 2 / (r - l), 0, 0, tx,
462 0, 2 / (t - b), 0, ty,
463 0, 0, 2 / (f - n), tz,
464 0, 0, 0, 1
465 );
466}
467
488mat4_t m4_perspective_RH(float vertical_field_of_view_in_deg, float aspect_ratio, float near_view_distance, float far_view_distance)
489{
490 float fovy_in_rad = vertical_field_of_view_in_deg / 180 * M_PI;
491 float f = 1.0f / tanf(fovy_in_rad / 2.0f);
492 float ar = aspect_ratio;
493 float nd = near_view_distance, fd = far_view_distance;
494
495 return mat4(
496 f / ar, 0, 0, 0,
497 0, f, 0, 0,
498 0, 0, (fd + nd) / (nd - fd), (2 * fd * nd) / (nd - fd),
499 0, 0, -1, 0
500 );
501}
502
539mat4_t m4_look_at_RH(vec3_t from, vec3_t to, vec3_t up)
540{
541 vec3_t z = v3_muls(v3_norm(v3_sub(to, from)), -1);
542 vec3_t x = v3_norm(v3_cross(up, z));
543 vec3_t y = v3_cross(z, x);
544
545 return mat4(
546 x.x, x.y, x.z, -v3_dot(from, x),
547 y.x, y.y, y.z, -v3_dot(from, y),
548 z.x, z.y, z.z, -v3_dot(from, z),
549 0, 0, 0, 1
550 );
551}
552
562mat4_t m4_frustum(float left, float right, float bottom, float top, float near, float far)
563{
564 /* Too much checking here ? */
565 if (fabs(left - right) < LG_FLOAT_EPSILON) {
566 INFO_ERR("left == right\n")
567 } else if (fabs(bottom - top) < LG_FLOAT_EPSILON) {
568 INFO_ERR("bottom == top\n")
569 } else if (far <= near) {
570 INFO_ERR("far <= near\n")
571 } else if (near <= 0.0f) {
572 INFO_ERR("near <= 0.0f\n")
573 } else if (far <= 0.0f) {
574 INFO_ERR("far <= 0.0f\n")
575 }
576
577 float width = right - left;
578 float height = top - bottom;
579 float depth = far - near;
580 float x = (2.0 * near) / width;
581 float y = (2.0 * near) / height;
582 float A = (right + left) / width;
583 float B = (top + bottom) / height;
584 float C = - (far + near) / depth;
585 float D = - (2.0 * far * near) / depth;
586
587 return mat4(
588 x, 0.0, A, 0.0,
589 0.0, y, B, 0.0,
590 0.0, 0.0, C, D,
591 0.0, 0.0, -1.0, 0.0
592 );
593}
594/* (until here) */
595
626mat4_t m4_invert_affine(mat4_t matrix)
627{
628 // Create shorthands to access matrix members
629 float m00 = matrix.m00, m10 = matrix.m10, m20 = matrix.m20, m30 = matrix.m30;
630 float m01 = matrix.m01, m11 = matrix.m11, m21 = matrix.m21, m31 = matrix.m31;
631 float m02 = matrix.m02, m12 = matrix.m12, m22 = matrix.m22, m32 = matrix.m32;
632
633 // Invert 3x3 part of the 4x4 matrix that contains the rotation, etc.
634 // That part is called R from here on.
635
636 // Calculate cofactor matrix of R
637 float c00 = m11 * m22 - m12 * m21, c10 = -(m01 * m22 - m02 * m21), c20 = m01 * m12 - m02 * m11;
638 float c01 = -(m10 * m22 - m12 * m20), c11 = m00 * m22 - m02 * m20, c21 = -(m00 * m12 - m02 * m10);
639 float c02 = m10 * m21 - m11 * m20, c12 = -(m00 * m21 - m01 * m20), c22 = m00 * m11 - m01 * m10;
640
641 // Caclculate the determinant by using the already calculated determinants
642 // in the cofactor matrix.
643 // Second sign is already minus from the cofactor matrix.
644 float det = m00 * c00 + m10 * c10 + m20 * c20;
645 if (fabsf(det) < 0.00001) {
646 return m4_identity();
647 }
648
649 // Calcuate inverse of R by dividing the transposed cofactor matrix by the
650 // determinant.
651 float i00 = c00 / det, i10 = c01 / det, i20 = c02 / det;
652 float i01 = c10 / det, i11 = c11 / det, i21 = c12 / det;
653 float i02 = c20 / det, i12 = c21 / det, i22 = c22 / det;
654
655 // Combine the inverted R with the inverted translation
656 return mat4(
657 i00, i10, i20, -(i00 * m30 + i10 * m31 + i20 * m32),
658 i01, i11, i21, -(i01 * m30 + i11 * m31 + i21 * m32),
659 i02, i12, i22, -(i02 * m30 + i12 * m31 + i22 * m32),
660 0, 0, 0, 1
661 );
662}
663
671vec3_t m4_mul_pos(mat4_t matrix, vec3_t position)
672{
673 vec3_t result = vec3(
674 matrix.m00 * position.x + matrix.m10 * position.y + matrix.m20 * position.z + matrix.m30,
675 matrix.m01 * position.x + matrix.m11 * position.y + matrix.m21 * position.z + matrix.m31,
676 matrix.m02 * position.x + matrix.m12 * position.y + matrix.m22 * position.z + matrix.m32
677 );
678
679 float w = matrix.m03 * position.x + matrix.m13 * position.y + matrix.m23 * position.z + matrix.m33;
680 if (w != 0 && w != 1) {
681 return vec3(result.x / w, result.y / w, result.z / w);
682 }
683
684 return result;
685}
686
698vec3_t m4_mul_dir(mat4_t matrix, vec3_t direction)
699{
700 vec3_t result = vec3(
701 matrix.m00 * direction.x + matrix.m10 * direction.y + matrix.m20 * direction.z,
702 matrix.m01 * direction.x + matrix.m11 * direction.y + matrix.m21 * direction.z,
703 matrix.m02 * direction.x + matrix.m12 * direction.y + matrix.m22 * direction.z
704 );
705
706 float w = matrix.m03 * direction.x + matrix.m13 * direction.y + matrix.m23 * direction.z;
707 if (w != 0 && w != 1) {
708 return vec3(result.x / w, result.y / w, result.z / w);
709 }
710
711 return result;
712}
713
714void m4_print(mat4_t matrix)
715{
716 m4_fprintp(STD_OUT, matrix, 6, 2);
717}
718
719void m4_printp(mat4_t matrix, int width, int precision)
720{
721 m4_fprintp(STD_OUT, matrix, width, precision);
722}
723
724void m4_fprint(FILE* stream, mat4_t matrix)
725{
726 m4_fprintp(stream, matrix, 6, 2);
727}
728
729void m4_fprintp(FILE* stream, mat4_t matrix, int width, int precision)
730{
731 mat4_t m = matrix;
732 int w = width, p = precision, r;
733
734 for (r = 0; r < 4; r++) {
735 fprintf(stream, "| %*.*f %*.*f %*.*f %*.*f |\n",
736 w, p, m.m[0][r], w, p, m.m[1][r], w, p, m.m[2][r], w, p, m.m[3][r]
737 );
738 }
739}
740
742void m4_print2(mat4_t matrix, const char *line_start)
743{
744 m4_fprintp2(STD_OUT, matrix, 6, 2, line_start);
745}
746
747void m4_printp2(mat4_t matrix, int width, int precision, const char *line_start)
748{
749 m4_fprintp2(STD_OUT, matrix, width, precision, line_start);
750}
751
752void m4_fprint2(FILE* stream, mat4_t matrix, const char *line_start)
753{
754 m4_fprintp2(stream, matrix, 6, 2, line_start);
755}
756void m4_fprintp2(FILE* stream, mat4_t matrix, int width, int precision, const char *line_start)
757{
758 mat4_t m = matrix;
759 int w = width, p = precision, r;
760
761 for (r = 0; r < 4; r++) {
762 fprintf(stream, "%s| %*.*f %*.*f %*.*f %*.*f |\n",
763 line_start, w, p, m.m[0][r], w, p, m.m[1][r], w, p, m.m[2][r], w, p, m.m[3][r]
764 );
765 }
766}
767
768#endif // MATH_3D_IMPLEMENTATION
Definition math_3d.h:123
Definition math_3d.h:179