a-game

2D platformer written from scratch.
git clone git://git.amin.space/a-game.git
Log | Files | Refs | README | LICENSE

commit 4fa6be5823c221c9d45734d1677174f4dd4a7c11
parent 487823facfe3c4477fc01b3951e369b3da1e6f12
Author: amin <dev@aminmesbah.com>
Date:   Sat, 27 Apr 2019 04:02:08 +0000

Rename glmth and its functions

FossilOrigin-Name: 7ae7bd95c220d2aaa0486d8056ce1bc1e2234481f78a02c38434b014454c6e2d
Diffstat:
Asrc/am_math.h | 625+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/game.c | 137++++++++++++++++++++++++++++++++++++++++---------------------------------------
Msrc/game.h | 2+-
Dsrc/glmth.h | 625-------------------------------------------------------------------------------
Msrc/shader.c | 2+-
5 files changed, 696 insertions(+), 695 deletions(-)

diff --git a/src/am_math.h b/src/am_math.h @@ -0,0 +1,625 @@ +#ifndef M_PI +#define M_PI 3.14159265359f +#endif + +// TODO: make sure these functions are inlineable and that the compiler does +// indeed inline them. + +internal inline f32 math_min(f32 a, f32 b) +{ + f32 min = a < b ? a : b; + return min; +} + +internal inline f32 math_max(f32 a, f32 b) +{ + f32 max = a > b ? a : b; + return max; +} + +internal inline i32 math_round(f32 n) +{ + return n < 0.0f ? n - 0.5 : n + 0.5; +} + +internal inline f32 math_wrap(f32 n, f32 min, f32 max) +{ + if (n > max) + { + return min; + } + else if (n < min) + { + return max; + } + else + { + return n; + } +} + +internal inline f32 math_rand(f32 min, f32 max) +{ + assert(min < max); + f32 random = ((f32) rand()) / (f32) RAND_MAX; + f32 diff = max - min; + f32 r = random * diff; + return min + r; +} + +internal inline m4 math_m4_init_id() +{ + m4 m = {0}; + m.E[0][0] = 1.0f; + m.E[1][1] = 1.0f; + m.E[2][2] = 1.0f; + m.E[3][3] = 1.0f; + return m; +} + +internal inline void math_m4_valueptr(m4 m, f32* out_valueptr) +{ + for (u8 v = 0; v < 16; ++v) + { + u8 row = v / 4; + u8 col = v % 4; + out_valueptr[v] = m.E[row][col]; + } +} + +internal inline bool math_m4m4_eq(m4 mat1, m4 mat2) +{ + for (u8 i = 0; i < 4; ++i) + { + for (u8 j = 0; j < 4; ++j) + { + if (mat1.E[i][j] != mat2.E[i][j]) + { + return false; + } + } + } + return true; +} + +internal inline m4 math_m4m4_m(m4 mat1, m4 mat2) +{ + m4 r = { + .E[0][0] = (mat1.E[0][0] * mat2.E[0][0]) + (mat1.E[0][1] * mat2.E[1][0]) + (mat1.E[0][2] * mat2.E[2][0]) + (mat1.E[0][3] * mat2.E[3][0]), + .E[0][1] = (mat1.E[0][0] * mat2.E[0][1]) + (mat1.E[0][1] * mat2.E[1][1]) + (mat1.E[0][2] * mat2.E[2][1]) + (mat1.E[0][3] * mat2.E[3][1]), + .E[0][2] = (mat1.E[0][0] * mat2.E[0][2]) + (mat1.E[0][1] * mat2.E[1][2]) + (mat1.E[0][2] * mat2.E[2][2]) + (mat1.E[0][3] * mat2.E[3][2]), + .E[0][3] = (mat1.E[0][0] * mat2.E[0][3]) + (mat1.E[0][1] * mat2.E[1][3]) + (mat1.E[0][2] * mat2.E[2][3]) + (mat1.E[0][3] * mat2.E[3][3]), + + .E[1][0] = (mat1.E[1][0] * mat2.E[0][0]) + (mat1.E[1][1] * mat2.E[1][0]) + (mat1.E[1][2] * mat2.E[2][0]) + (mat1.E[1][3] * mat2.E[3][0]), + .E[1][1] = (mat1.E[1][0] * mat2.E[0][1]) + (mat1.E[1][1] * mat2.E[1][1]) + (mat1.E[1][2] * mat2.E[2][1]) + (mat1.E[1][3] * mat2.E[3][1]), + .E[1][2] = (mat1.E[1][0] * mat2.E[0][2]) + (mat1.E[1][1] * mat2.E[1][2]) + (mat1.E[1][2] * mat2.E[2][2]) + (mat1.E[1][3] * mat2.E[3][2]), + .E[1][3] = (mat1.E[1][0] * mat2.E[0][3]) + (mat1.E[1][1] * mat2.E[1][3]) + (mat1.E[1][2] * mat2.E[2][3]) + (mat1.E[1][3] * mat2.E[3][3]), + + .E[2][0] = (mat1.E[2][0] * mat2.E[0][0]) + (mat1.E[2][1] * mat2.E[1][0]) + (mat1.E[2][2] * mat2.E[2][0]) + (mat1.E[2][3] * mat2.E[3][0]), + .E[2][1] = (mat1.E[2][0] * mat2.E[0][1]) + (mat1.E[2][1] * mat2.E[1][1]) + (mat1.E[2][2] * mat2.E[2][1]) + (mat1.E[2][3] * mat2.E[3][1]), + .E[2][2] = (mat1.E[2][0] * mat2.E[0][2]) + (mat1.E[2][1] * mat2.E[1][2]) + (mat1.E[2][2] * mat2.E[2][2]) + (mat1.E[2][3] * mat2.E[3][2]), + .E[2][3] = (mat1.E[2][0] * mat2.E[0][3]) + (mat1.E[2][1] * mat2.E[1][3]) + (mat1.E[2][2] * mat2.E[2][3]) + (mat1.E[2][3] * mat2.E[3][3]), + + .E[3][0] = (mat1.E[3][0] * mat2.E[0][0]) + (mat1.E[3][1] * mat2.E[1][0]) + (mat1.E[3][2] * mat2.E[2][0]) + (mat1.E[3][3] * mat2.E[3][0]), + .E[3][1] = (mat1.E[3][0] * mat2.E[0][1]) + (mat1.E[3][1] * mat2.E[1][1]) + (mat1.E[3][2] * mat2.E[2][1]) + (mat1.E[3][3] * mat2.E[3][1]), + .E[3][2] = (mat1.E[3][0] * mat2.E[0][2]) + (mat1.E[3][1] * mat2.E[1][2]) + (mat1.E[3][2] * mat2.E[2][2]) + (mat1.E[3][3] * mat2.E[3][2]), + .E[3][3] = (mat1.E[3][0] * mat2.E[0][3]) + (mat1.E[3][1] * mat2.E[1][3]) + (mat1.E[3][2] * mat2.E[2][3]) + (mat1.E[3][3] * mat2.E[3][3]), + }; + return r; +} + +internal inline v4 math_m4v4_m(m4 m, v4 v) +{ + v4 r = { + .x = (m.E[0][0] * v.x) + (m.E[0][1] * v.y) + (m.E[0][2] * v.z) + (m.E[0][3] * v.w), + .y = (m.E[1][0] * v.x) + (m.E[1][1] * v.y) + (m.E[1][2] * v.z) + (m.E[1][3] * v.w), + .z = (m.E[2][0] * v.x) + (m.E[2][1] * v.y) + (m.E[2][2] * v.z) + (m.E[2][3] * v.w), + .w = (m.E[3][0] * v.x) + (m.E[3][1] * v.y) + (m.E[3][2] * v.z) + (m.E[3][3] * v.w), + }; + return r; +} + +internal inline bool math_approx(f32 n, f32 compare, f32 epsilon) +{ + return fabsf(n - compare) <= epsilon; +} + +internal inline bool math_v2_is_nonzero(v2 v) +{ + // TODO: Figure out whether this epsilon is universally appropriate. + f32 epsilon = 0.000001; + return ( + !math_approx(v.x, 0.0f, epsilon) + || !math_approx(v.y, 0.0f, epsilon) + ); +} + +internal inline v2 math_v2_a(v2 vec1, v2 vec2) +{ + v2 r = { + .x = vec1.x + vec2.x, + .y = vec1.y + vec2.y, + }; + return r; +} + +internal inline v2 math_v2_negate(v2 v) +{ + v2 r = {-v.x, -v.y}; + return r; +} + +internal inline v2 math_v2_s(v2 vec1, v2 vec2) +{ + return math_v2_a(vec1, math_v2_negate(vec2)); +} + +internal inline v2 math_v2f_m(v2 v, f32 s) +{ + v2 r = {v.x * s, v.y * s}; + return r; +} + +internal inline f32 math_v2_length(v2 v) +{ + // TODO: Use inner product of v with itself + return sqrtf((v.x * v.x) + (v.y * v.y)); +} + +// TODO: handle degenerate case where l is near 0.0f +// TODO: scale by 1.0f / l +internal inline v2 math_v2_normalize(v2 v) +{ + f32 l = math_v2_length(v); + v2 r = {v.x / l, v.y / l}; + return r; +} + +internal inline f32 math_v2_dot(v2 vec1, v2 vec2) +{ + // dot product, a.k.a. inner product or scalar product + f32 dot_product = (vec1.x * vec2.x) + (vec1.y * vec2.y); + // NOTE: this value happens to be: + // |vec1| * |vec2| * Cos(theta) + // where theta is the angle between vec1 and vec2. This is useful in all + // sorts of ways: What if theta is zero? What if vec1 or vec2 is a unit + // vector? + return dot_product; +} + +internal inline v2i math_v2i_a(v2i vec1, v2i vec2) +{ + v2i r = {vec1.x + vec2.x, vec1.y + vec2.y}; + return r; +} + +internal inline v3 math_v3_cross(v3 vec1, v3 vec2) +{ + // cross product, a.k.a. outer product or vector product + v3 r = { + .x = (vec1.y * vec2.z) - (vec1.z * vec2.y), + .y = (vec1.z * vec2.x) - (vec1.x * vec2.z), + .z = (vec1.x * vec2.y) - (vec1.y * vec2.x), + }; + return r; +} + +internal inline f32 math_v3_length(v3 v) +{ + // TODO: Use inner product of v with itself + // NOTE: math_v3_dot(v, v) returns: + // |v| * |v| * Cos(theta) + // We know that theta, the angle between v and itself, is always 0, so we + // know that Cos(theta) is always 1. We can then simplify the above + // expression to be: + // (|v|)^2 + // so sqrtf(math_v3_dot(v, v)) is the length of v + return sqrtf((v.x * v.x) + (v.y * v.y) + (v.z * v.z)); +} + +internal inline v3 math_v3f_m(v3 v, f32 s) +{ + v3 r = {.x = v.x * s, .y = v.y * s, .z = v.z * s}; + return r; +} + +internal inline v3 math_v3_negate(v3 v) +{ + v3 r = {.x = -v.x, .y = -v.y, .z = -v.z}; + return r; +} + +// TODO: handle degenerate case where l is near 0.0f +// TODO: scale by 1.0f / l +internal inline v3 math_v3_normalize(v3 v) +{ + f32 l = math_v3_length(v); + v3 r = {v.x / l, v.y / l, v.z / l}; + return r; +} + +internal inline v3 math_v3_a(v3 vec1, v3 vec2) +{ + v3 r = { + .x = vec1.x + vec2.x, + .y = vec1.y + vec2.y, + .z = vec1.z + vec2.z + }; + return r; +} + +internal inline v3 math_v3_s(v3 vec1, v3 vec2) +{ + return math_v3_a(vec1, math_v3_negate(vec2)); +} + +internal inline bool math_v4v4_eq(v4 vec1, v4 vec2) +{ + for (u8 i = 0; i < 4; ++i) + { + if (vec1.E[i] != vec2.E[i]) + { + return false; + } + } + return true; +} + +internal inline void math_clamp(f32 *f, f32 min, f32 max) +{ + if (*f < min) + { + *f = min; + } + else if (*f > max) + { + *f = max; + } +} + +internal inline f32 math_deg(f32 rad) +{ + return rad * (180.0f / M_PI); +} + +internal inline f32 math_lerpf(f32 f, f32 min, f32 max) +{ + assert(f >= 0.0f && f <= 1.0f); + return (1.0f - f) * min + f * max; +} + +internal inline f32 math_rad(f32 deg) +{ + return deg * (M_PI / 180.0f); +} + +internal inline m4 math_rotate_x(m4 m, f32 rad) +{ + f32 c = cosf(rad); + f32 s = sinf(rad); + + m4 r = math_m4_init_id(); + + r.E[1][1] = c; + r.E[1][2] = -s; + + r.E[2][1] = s; + r.E[2][2] = c; + + return math_m4m4_m(m, r); +} + +internal inline m4 math_rotate_y(m4 m, f32 rad) +{ + f32 c = cosf(rad); + f32 s = sinf(rad); + + m4 r = math_m4_init_id(); + + r.E[0][0] = c; + r.E[0][2] = s; + + r.E[2][0] = -s; + r.E[2][2] = c; + + return math_m4m4_m(m, r); +} + +internal inline m4 math_rotate_z(m4 m, f32 rad) +{ + f32 c = cosf(rad); + f32 s = sinf(rad); + + m4 r = math_m4_init_id(); + + r.E[0][0] = c; + r.E[0][1] = -s; + + r.E[1][0] = s; + r.E[1][1] = c; + + return math_m4m4_m(m, r); +} + +internal inline m4 math_rotate(m4 m, f32 rad, v3 axis) +{ + axis = math_v3_normalize(axis); + + f32 c = cosf(rad); + f32 s = sinf(rad); + + m4 r = math_m4_init_id(); + + r.E[0][0] = c + (powf(axis.x, 2.0f) * (1 - c)); + r.E[0][1] = (axis.x * axis.y * (1 - c)) - (axis.z * s); + r.E[0][2] = (axis.x * axis.z * (1 - c)) + (axis.y * s); + + r.E[1][0] = (axis.y * axis.x * (1 - c)) + (axis.z * s); + r.E[1][1] = c + (powf(axis.y, 2.0f) * (1 - c)); + r.E[1][2] = (axis.y * axis.z * (1 - c)) - (axis.x * s); + + r.E[2][0] = (axis.z * axis.x * (1 - c)) - (axis.y * s); + r.E[2][1] = (axis.z * axis.y * (1 - c)) + (axis.x * s); + r.E[2][2] = c + (powf(axis.z, 2.0f) * (1 - c)); + + return math_m4m4_m(m, r); +} + +internal inline m4 math_scale(m4 m, v3 v) +{ + m4 r = math_m4_init_id(); + r.E[0][0] = v.x; + r.E[1][1] = v.y; + r.E[2][2] = v.z; + return math_m4m4_m(m, r); +} + +internal inline m4 math_translate(m4 m, v3 v) +{ + m4 r = math_m4_init_id(); + r.E[0][3] = v.x; + r.E[1][3] = v.y; + r.E[2][3] = v.z; + return math_m4m4_m(m, r); +} + +internal inline m4 math_projection_ortho(f32 left, f32 right, f32 bottom, f32 top, f32 near, f32 far) +{ + // TODO: This assert fails when you minimise the window in Windows. + assert(left != right); + assert(bottom != top); + assert(near != far); + + m4 r = math_m4_init_id(); + + r.E[0][0] = 2.0f / (right - left); + r.E[0][1] = 0.0f; + r.E[0][2] = 0.0f; + r.E[0][3] = -(right + left) / (right - left); + + r.E[1][0] = 0.0f; + r.E[1][1] = 2.0f / (top - bottom); + r.E[1][2] = 0.0f; + r.E[1][3] = -(top + bottom) / (top - bottom); + + r.E[2][0] = 0.0f; + r.E[2][1] = 0.0f; + r.E[2][2] = -2.0f / (far - near); + r.E[2][3] = -(far + near) / (far - near); + + r.E[3][0] = 0.0f; + r.E[3][1] = 0.0f; + r.E[3][2] = 0.0f; + r.E[3][3] = 1.0f; + + return r; +} + +internal inline m4 math_projection_perspective(f32 left, f32 right, f32 bottom, f32 top, f32 near, f32 far) +{ + assert(left != right); + assert(bottom != top); + assert(near != far); + + m4 r = math_m4_init_id(); + + r.E[0][0] = (2.0f * near) / (right - left); + r.E[0][1] = 0.0f; + r.E[0][2] = (right + left) / (right - left); + r.E[0][3] = 0.0f; + + r.E[1][0] = 0.0f; + r.E[1][1] = (2.0f * near) / (top - bottom); + r.E[1][2] = (top + bottom) / (top - bottom); + r.E[1][3] = 0.0f; + + r.E[2][0] = 0.0f; + r.E[2][1] = 0.0f; + r.E[2][2] = -(far + near) / (far - near); + r.E[2][3] = (-2.0f * far * near) / (far - near); + + r.E[3][0] = 0.0f; + r.E[3][1] = 0.0f; + r.E[3][2] = -1.0f; + r.E[3][3] = 0.0f; + + return r; +} + +internal inline m4 math_projection_perspective_fov(f32 fovy, f32 aspect, f32 near, f32 far) +{ + f32 half_height = tanf(fovy / 2.0f) * near; + f32 half_width = half_height * aspect; + f32 left = -half_width; + f32 right = half_width; + f32 bottom = -half_height; + f32 top = half_height; + + return math_projection_perspective(left, right, bottom, top, near, far); +} + +internal inline m4 math_camera_look_at(v3 camera_pos, v3 camera_target, v3 up) +{ + v3 camera_direction = math_v3_normalize(math_v3_s(camera_pos, camera_target)); + v3 camera_right = math_v3_normalize(math_v3_cross(up, camera_direction)); + v3 camera_up = math_v3_cross(camera_direction, camera_right); + + m4 look = math_m4_init_id(); + look.E[0][0] = camera_right.x; + look.E[0][1] = camera_right.y; + look.E[0][2] = camera_right.z; + + look.E[1][0] = camera_up.x; + look.E[1][1] = camera_up.y; + look.E[1][2] = camera_up.z; + + look.E[2][0] = camera_direction.x; + look.E[2][1] = camera_direction.y; + look.E[2][2] = camera_direction.z; + + return math_m4m4_m(look, math_translate(math_m4_init_id(), math_v3_negate(camera_pos))); +} + +internal inline bool math_intersect_aabb_aabb(rect r1, rect r2) +{ + f32 total_w = (r1.max.x - r1.min.x) + (r2.max.x - r2.min.x); + f32 total_h = (r1.max.y - r1.min.y) + (r2.max.y - r2.min.y); + bool aabbs_do_indeed_intersect = ( + fabsf(r1.max.x - r2.min.x) <= total_w + && fabsf(r1.max.y - r2.min.y) <= total_h + && fabsf(r2.max.x - r1.min.x) <= total_w + && fabsf(r2.max.y - r1.min.y) <= total_h + ); + return aabbs_do_indeed_intersect; +} + +internal inline rect math_minkowski_sum_rect_rect(rect r1, v2 r2_dimensions) +{ + f32 r2_half_w = 0.5f * r2_dimensions.width; + f32 r2_half_h = 0.5f * r2_dimensions.height; + rect sum = { + .min = {r1.min.x - r2_half_w, r1.min.y - r2_half_h}, + .max = {r1.max.x + r2_half_w, r1.max.y + r2_half_h}, + }; + return sum; +} + +enum MathRectEdge +{ + RECT_EDGE_BOTTOM, + RECT_EDGE_TOP, + RECT_EDGE_LEFT, + RECT_EDGE_RIGHT, +}; + +internal inline segment math_rect_get_edge(rect r, enum MathRectEdge e) +{ + v2 nw = {r.min.x, r.max.y}; + v2 ne = r.max; + v2 sw = r.min; + v2 se = {r.max.x, r.min.y}; + + segment edge = {0}; + switch(e) + { + case RECT_EDGE_BOTTOM: + edge = (segment) {sw, se}; + break; + case RECT_EDGE_TOP: + edge = (segment) {nw, ne}; + break; + case RECT_EDGE_LEFT: + edge = (segment) {sw, nw}; + break; + case RECT_EDGE_RIGHT: + edge = (segment) {se, ne}; + break; + default: + assert(false); + break; + } + return edge; +} + +internal inline bool math_in_interval_open(f32 n, f32 min, f32 max) +{ + assert(min <= max); + bool in_open_interval = false; + in_open_interval = (n >= min && n <= max); + return in_open_interval; +} + +internal inline bool math_v2_lt(v2 v, v2 compare) +{ + bool is_lt = false; + is_lt = (v.x < compare.x && v.y < compare.y); + return is_lt; +} + +internal inline bool math_v2_le(v2 v, v2 compare) +{ + bool is_le = false; + is_le = (v.x <= compare.x && v.y <= compare.y); + return is_le; +} + +internal inline bool math_v2_ge(v2 v, v2 compare) +{ + bool is_ge = false; + is_ge = (v.x >= compare.x && v.y >= compare.y); + return is_ge; +} + +internal inline void math_v2_print(v2 v) +{ + printf("( %f, %f )\n", v.x, v.y); +} + +internal inline void math_v2u_print(v2u v) +{ + printf("( %u, %u )\n", v.x, v.y); +} + +internal inline void math_v2i_print(v2i v) +{ + printf("( %i, %i )\n", v.x, v.y); +} + +internal inline void math_v3_print(v3 v) +{ + printf("( %f, %f, %f )\n", v.x, v.y, v.z); +} + +internal inline void math_v4_print(v4 v) +{ + printf("( %f, %f, %f, %f )\n", v.x, v.y, v.z, v.w); +} + +internal inline void math_rect_print(rect r) +{ + printf("rect:\n"); + printf(" min: "); + math_v2_print(r.min); + printf(" max: "); + math_v2_print(r.max); +} + +internal inline void math_m4_print(m4 m) +{ + printf("[\n"); + printf(" %f, %f, %f, %f\n", m.E[0][0], m.E[0][1], m.E[0][2], m.E[0][3]); + printf(" %f, %f, %f, %f\n", m.E[1][0], m.E[1][1], m.E[1][2], m.E[1][3]); + printf(" %f, %f, %f, %f\n", m.E[2][0], m.E[2][1], m.E[2][2], m.E[2][3]); + printf(" %f, %f, %f, %f\n", m.E[3][0], m.E[3][1], m.E[3][2], m.E[3][3]); + printf("]\n"); +} + +#define math_print(x) _Generic((x),\ + v2: math_v2_print, \ + v2u: math_v2u_print, \ + v2i: math_v2i_print, \ + v3: math_v3_print, \ + v4: math_v4_print, \ + rect: math_rect_print, \ + m4: math_m4_print \ + )(x) diff --git a/src/game.c b/src/game.c @@ -97,26 +97,26 @@ internal struct AbsolutePos pos_recanonicalize(struct AbsolutePos p) if (p.local.x < 0.0f) { room_index_delta.x -= 1; - p_final.local.x = glmth_wrap(p.local.x, 0.0f, ROOM_TILE_DIM_X); + p_final.local.x = math_wrap(p.local.x, 0.0f, ROOM_TILE_DIM_X); } else if (p.local.x > ROOM_TILE_DIM_X) { room_index_delta.x += 1; - p_final.local.x = glmth_wrap(p.local.x, 0.0f, ROOM_TILE_DIM_X); + p_final.local.x = math_wrap(p.local.x, 0.0f, ROOM_TILE_DIM_X); } if (p.local.y < 0.0f) { room_index_delta.y -= 1; - p_final.local.y = glmth_wrap(p.local.y, 0.0f, ROOM_TILE_DIM_Y); + p_final.local.y = math_wrap(p.local.y, 0.0f, ROOM_TILE_DIM_Y); } else if (p.local.y > ROOM_TILE_DIM_Y) { room_index_delta.y += 1; - p_final.local.y = glmth_wrap(p.local.y, 0.0f, ROOM_TILE_DIM_Y); + p_final.local.y = math_wrap(p.local.y, 0.0f, ROOM_TILE_DIM_Y); } - p_final.room = glmth_v2i_a(p.room, room_index_delta); + p_final.room = math_v2i_a(p.room, room_index_delta); return p_final; } @@ -153,7 +153,7 @@ internal struct WallCollision get_wall_collision(v2 entity_p_initial, v2 entity_ struct WallCollision result = {0}; result.collision_occurred = false; - assert(glmth_v2_le(wall.min, wall.max)); + assert(math_v2_le(wall.min, wall.max)); bool wall_is_horizontal = wall.min.y == wall.max.y; bool wall_is_vertical = wall.min.x == wall.max.x; @@ -167,14 +167,14 @@ internal struct WallCollision get_wall_collision(v2 entity_p_initial, v2 entity_ wall_normal_axis = AXIS_X; } - v2 player_delta = glmth_v2_s(entity_p_final, entity_p_initial); + v2 player_delta = math_v2_s(entity_p_final, entity_p_initial); // NOTE(amin): We needn't normalize player_delta here because its magnitude // becomes irrelevant as the value of cos(theta) nears zero, and we are // concerned only about the behavior near pi/2 and 3pi/2, where cos(theta) // is zero. Therefore we simply treat player_delta as if it _were_ // normalized. - f32 cos_theta = glmth_v2_dot(player_delta, wall_normal); + f32 cos_theta = math_v2_dot(player_delta, wall_normal); bool theta_is_obtuse = cos_theta < 0.0f; if (theta_is_obtuse) @@ -185,17 +185,17 @@ internal struct WallCollision get_wall_collision(v2 entity_p_initial, v2 entity_ f32 initial_d = wall_position - entity_p_initial.E[wall_normal_axis]; f32 segment_scale_factor = initial_d / player_delta.E[wall_normal_axis]; - v2 collision_point = glmth_v2_a(entity_p_initial, glmth_v2f_m(player_delta, segment_scale_factor)); + v2 collision_point = math_v2_a(entity_p_initial, math_v2f_m(player_delta, segment_scale_factor)); - if (glmth_in_interval_open(segment_scale_factor, 0.0f, 1.0f)) + if (math_in_interval_open(segment_scale_factor, 0.0f, 1.0f)) { - if (glmth_in_interval_open( + if (math_in_interval_open( collision_point.E[wall_axis], wall.min.E[wall_axis], wall.max.E[wall_axis])) { result.collision_occurred = true; - result.distance_scale_factor = glmth_max(0.0f, segment_scale_factor); + result.distance_scale_factor = math_max(0.0f, segment_scale_factor); } } } @@ -212,11 +212,11 @@ internal u32 get_tile_value(u32 *tiles, v2 tile_pos) ROOM_TILE_DIM_Y - 1.0f - tile_pos.y, }; - assert(glmth_v2_lt( + assert(math_v2_lt( (v2) {tile_i_2d.x, tile_i_2d.y}, (v2) {ROOM_TILE_DIM_X, ROOM_TILE_DIM_Y})); - assert(glmth_v2_ge( + assert(math_v2_ge( (v2) {tile_i_2d.x, tile_i_2d.y}, (v2) {0})); @@ -246,8 +246,8 @@ internal bool wall_is_screen_edge(u32 *tiles, segment wall) bool wall_is_vertical = wall.min.x == wall.max.x; assert(wall_is_vertical || wall_is_horizontal); - assert(glmth_v2_ge(wall.min, (v2) {0})); - assert(glmth_v2_le(wall.max, (v2) {ROOM_TILE_DIM_X, ROOM_TILE_DIM_Y})); + assert(math_v2_ge(wall.min, (v2) {0})); + assert(math_v2_le(wall.max, (v2) {ROOM_TILE_DIM_X, ROOM_TILE_DIM_Y})); if (wall_is_vertical) { @@ -323,7 +323,7 @@ void game_update_and_render(struct GameMemory *game_memory, struct GameInput *ga v_pad_size, }; - viewport.max = glmth_v2_a(viewport.min, viewport_size); + viewport.max = math_v2_a(viewport.min, viewport_size); } glClearColor(0.1f, 0.1f, 0.1f, 1.0f); @@ -334,15 +334,15 @@ void game_update_and_render(struct GameMemory *game_memory, struct GameInput *ga glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); #endif - m4 view = glmth_m4_init_id(); + m4 view = math_m4_init_id(); // World origin is in the lower left - view = glmth_translate(view, (v3) {0.0f, framebuffer.height, 0.0f}); - view = glmth_scale(view, (v3) {1.0f, -1.0f, 1.0f}); - view = glmth_translate(view, (v3) {viewport.min.x, viewport.min.y, 0.0f}); - view = glmth_scale(view, (v3) {ppm, ppm, 1.0f}); + view = math_translate(view, (v3) {0.0f, framebuffer.height, 0.0f}); + view = math_scale(view, (v3) {1.0f, -1.0f, 1.0f}); + view = math_translate(view, (v3) {viewport.min.x, viewport.min.y, 0.0f}); + view = math_scale(view, (v3) {ppm, ppm, 1.0f}); // Screen origin is in the upper left - m4 projection = glmth_projection_ortho(0.0f, framebuffer.width, framebuffer.height, 0.0f, -1.0f, 0.0f); + m4 projection = math_projection_ortho(0.0f, framebuffer.width, framebuffer.height, 0.0f, -1.0f, 0.0f); struct Room knytt_hanging_fly_village = { .index = {0, 0}, @@ -401,13 +401,13 @@ void game_update_and_render(struct GameMemory *game_memory, struct GameInput *ga ROOM_TILE_DIM_Y - 1.0f - y, }; - m4 model = glmth_m4_init_id(); + m4 model = math_m4_init_id(); // our square verts are anchored around the center point of the // square, so we want to offset by 0.5 to instead have our // anchor in the min corner - model = glmth_translate(model, (v3) {TILE_SIZE * 0.5f, TILE_SIZE * 0.5f, 0.0f}); - model = glmth_translate(model, (v3) {tile_pos.x, tile_pos.y, 0.0f}); - model = glmth_scale(model, (v3) {TILE_SIZE, TILE_SIZE, 1.0f}); + model = math_translate(model, (v3) {TILE_SIZE * 0.5f, TILE_SIZE * 0.5f, 0.0f}); + model = math_translate(model, (v3) {tile_pos.x, tile_pos.y, 0.0f}); + model = math_scale(model, (v3) {TILE_SIZE, TILE_SIZE, 1.0f}); v3 color; @@ -434,7 +434,7 @@ void game_update_and_render(struct GameMemory *game_memory, struct GameInput *ga .max = {tile_pos.x + TILE_SIZE, tile_pos.y + TILE_SIZE}, }; - bool player_is_in_tile = glmth_intersect_aabb_aabb(player_aabb, tile_aabb); + bool player_is_in_tile = math_intersect_aabb_aabb(player_aabb, tile_aabb); if (player_is_in_tile) { color = (v3) {0.4f, 0.8f, 0.4f}; @@ -503,30 +503,31 @@ void game_update_and_render(struct GameMemory *game_memory, struct GameInput *ga } // Semi implicit Euler integration: https://gafferongames.com/post/integration_basics/ - player->velocity = glmth_v2_a(player->velocity, glmth_v2f_m(player->acceleration, dt)); - glmth_clamp(&player->velocity.x, -max_meters_per_second, max_meters_per_second); - glmth_clamp(&player->velocity.y, -max_meters_per_second, max_meters_per_second); + player->velocity = math_v2_a(player->velocity, math_v2f_m(player->acceleration, dt)); + // TODO: clamp the length of the velocity vector, not each of its components + math_clamp(&player->velocity.x, -max_meters_per_second, max_meters_per_second); + math_clamp(&player->velocity.y, -max_meters_per_second, max_meters_per_second); { #define RENDER_COLLISION_DEBUG_QUAD(r, c) render_debug_quad(game_state, (r), (c), &view, &projection); - v2 player_delta = glmth_v2f_m(player->velocity, dt); - v2 new_p = glmth_v2_a(player->pos.local, player_delta); + v2 player_delta = math_v2f_m(player->velocity, dt); + v2 new_p = math_v2_a(player->pos.local, player_delta); rect player_traversal_bb = { - .min = {glmth_min(player->pos.local.x, new_p.x), glmth_min(player->pos.local.y, new_p.y)}, - .max = {glmth_max(player->pos.local.x, new_p.x), glmth_max(player->pos.local.y, new_p.y)}, + .min = {math_min(player->pos.local.x, new_p.x), math_min(player->pos.local.y, new_p.y)}, + .max = {math_max(player->pos.local.x, new_p.x), math_max(player->pos.local.y, new_p.y)}, }; - rect player_traversal_occupancy_bb = glmth_minkowski_sum_rect_rect(player_traversal_bb, player->dimensions); + rect player_traversal_occupancy_bb = math_minkowski_sum_rect_rect(player_traversal_bb, player->dimensions); rect tile_search_range = { .min = { - (i32)glmth_max(0.0f, player_traversal_occupancy_bb.min.x - 1), - (i32)glmth_max(0.0f, player_traversal_occupancy_bb.min.y - 1), + (i32)math_max(0.0f, player_traversal_occupancy_bb.min.x - 1), + (i32)math_max(0.0f, player_traversal_occupancy_bb.min.y - 1), }, .max = { - (i32)glmth_min(ROOM_TILE_DIM_X, player_traversal_occupancy_bb.max.x + 2), - (i32)glmth_min(ROOM_TILE_DIM_Y, player_traversal_occupancy_bb.max.y + 2), + (i32)math_min(ROOM_TILE_DIM_X, player_traversal_occupancy_bb.max.x + 2), + (i32)math_min(ROOM_TILE_DIM_Y, player_traversal_occupancy_bb.max.y + 2), }, }; @@ -555,20 +556,20 @@ void game_update_and_render(struct GameMemory *game_memory, struct GameInput *ga .max = {tile_x + TILE_SIZE, tile_y + TILE_SIZE}, }; - rect tile_player_sum = glmth_minkowski_sum_rect_rect(tile_aabb, player->dimensions); + rect tile_player_sum = math_minkowski_sum_rect_rect(tile_aabb, player->dimensions); RENDER_COLLISION_DEBUG_QUAD(tile_player_sum, ((v3) {0.8f, 0.4f, 0.4f})); - segment tile_sum_b = glmth_rect_get_edge(tile_player_sum, RECT_EDGE_BOTTOM); - segment tile_sum_t = glmth_rect_get_edge(tile_player_sum, RECT_EDGE_TOP); - segment tile_sum_l = glmth_rect_get_edge(tile_player_sum, RECT_EDGE_LEFT); - segment tile_sum_r = glmth_rect_get_edge(tile_player_sum, RECT_EDGE_RIGHT); + segment tile_sum_b = math_rect_get_edge(tile_player_sum, RECT_EDGE_BOTTOM); + segment tile_sum_t = math_rect_get_edge(tile_player_sum, RECT_EDGE_TOP); + segment tile_sum_l = math_rect_get_edge(tile_player_sum, RECT_EDGE_LEFT); + segment tile_sum_r = math_rect_get_edge(tile_player_sum, RECT_EDGE_RIGHT); v2 norm_b = {0.0f, -1.0f}; v2 norm_t = {0.0f, 1.0f}; v2 norm_l = {-1.0f, 0.0f}; v2 norm_r = {1.0f, 0.0f}; - if (!wall_is_screen_edge(tiles, glmth_rect_get_edge(tile_aabb, RECT_EDGE_BOTTOM)) && !wall_is_internal(tiles, glmth_rect_get_edge(tile_aabb, RECT_EDGE_BOTTOM))) + if (!wall_is_screen_edge(tiles, math_rect_get_edge(tile_aabb, RECT_EDGE_BOTTOM)) && !wall_is_internal(tiles, math_rect_get_edge(tile_aabb, RECT_EDGE_BOTTOM))) { struct WallCollision bottom = get_wall_collision(player->pos.local, new_p, tile_sum_b, norm_b); if (bottom.collision_occurred) @@ -585,7 +586,7 @@ void game_update_and_render(struct GameMemory *game_memory, struct GameInput *ga RENDER_COLLISION_DEBUG_QUAD(collision, ((v3) {1.0f, 0.0f, 0.0f})); } } - if (!wall_is_screen_edge(tiles, glmth_rect_get_edge(tile_aabb, RECT_EDGE_TOP)) && !wall_is_internal(tiles, glmth_rect_get_edge(tile_aabb, RECT_EDGE_TOP))) + if (!wall_is_screen_edge(tiles, math_rect_get_edge(tile_aabb, RECT_EDGE_TOP)) && !wall_is_internal(tiles, math_rect_get_edge(tile_aabb, RECT_EDGE_TOP))) { struct WallCollision top = get_wall_collision(player->pos.local, new_p, tile_sum_t, norm_t); if (top.collision_occurred) @@ -602,7 +603,7 @@ void game_update_and_render(struct GameMemory *game_memory, struct GameInput *ga RENDER_COLLISION_DEBUG_QUAD(collision, ((v3) {0.0f, 1.0f, 0.0f})); } } - if (!wall_is_screen_edge(tiles, glmth_rect_get_edge(tile_aabb, RECT_EDGE_LEFT)) && !wall_is_internal(tiles, glmth_rect_get_edge(tile_aabb, RECT_EDGE_LEFT))) + if (!wall_is_screen_edge(tiles, math_rect_get_edge(tile_aabb, RECT_EDGE_LEFT)) && !wall_is_internal(tiles, math_rect_get_edge(tile_aabb, RECT_EDGE_LEFT))) { struct WallCollision left = get_wall_collision(player->pos.local, new_p, tile_sum_l, norm_l); if (left.collision_occurred) @@ -619,7 +620,7 @@ void game_update_and_render(struct GameMemory *game_memory, struct GameInput *ga RENDER_COLLISION_DEBUG_QUAD(collision, ((v3) {0.0f, 0.0f, 1.0f})); } } - if (!wall_is_screen_edge(tiles, glmth_rect_get_edge(tile_aabb, RECT_EDGE_RIGHT)) && !wall_is_internal(tiles, glmth_rect_get_edge(tile_aabb, RECT_EDGE_RIGHT))) + if (!wall_is_screen_edge(tiles, math_rect_get_edge(tile_aabb, RECT_EDGE_RIGHT)) && !wall_is_internal(tiles, math_rect_get_edge(tile_aabb, RECT_EDGE_RIGHT))) { struct WallCollision right = get_wall_collision(player->pos.local, new_p, tile_sum_r, norm_r); if (right.collision_occurred) @@ -640,24 +641,24 @@ void game_update_and_render(struct GameMemory *game_memory, struct GameInput *ga } } - v2 delta_to_nearest_collision = glmth_v2f_m(player_delta, smallest_distance_scale_factor); - v2 nearest_collision_p = glmth_v2_a(player->pos.local, delta_to_nearest_collision); + v2 delta_to_nearest_collision = math_v2f_m(player_delta, smallest_distance_scale_factor); + v2 nearest_collision_p = math_v2_a(player->pos.local, delta_to_nearest_collision); f32 epsilon = 0.0001f; rect local_inhabitable_region = { .min = { - glmth_min(player->pos.local.x, nearest_collision_p.x + epsilon), - glmth_min(player->pos.local.y, nearest_collision_p.y + epsilon), + math_min(player->pos.local.x, nearest_collision_p.x + epsilon), + math_min(player->pos.local.y, nearest_collision_p.y + epsilon), }, .max = { - glmth_max(player->pos.local.x, nearest_collision_p.x - epsilon), - glmth_max(player->pos.local.y, nearest_collision_p.y - epsilon), + math_max(player->pos.local.x, nearest_collision_p.x - epsilon), + math_max(player->pos.local.y, nearest_collision_p.y - epsilon), }, }; player->pos.local = nearest_collision_p; - glmth_clamp(&player->pos.local.x, local_inhabitable_region.min.x, local_inhabitable_region.max.x); - glmth_clamp(&player->pos.local.y, local_inhabitable_region.min.y, local_inhabitable_region.max.y); + math_clamp(&player->pos.local.x, local_inhabitable_region.min.x, local_inhabitable_region.max.x); + math_clamp(&player->pos.local.y, local_inhabitable_region.min.y, local_inhabitable_region.max.y); // NOTE(amin): // n * dot(v, n) @@ -666,12 +667,12 @@ void game_update_and_render(struct GameMemory *game_memory, struct GameInput *ga // = n * (|v| * cos(theta)) // = n * |v_projected_onto_n| // = v_projected_onto_n - v2 colliding_velocity_component = glmth_v2f_m(wall_normal, glmth_v2_dot(player->velocity, wall_normal)); + v2 colliding_velocity_component = math_v2f_m(wall_normal, math_v2_dot(player->velocity, wall_normal)); - player->velocity = glmth_v2_s(player->velocity, colliding_velocity_component); + player->velocity = math_v2_s(player->velocity, colliding_velocity_component); - player_delta = glmth_v2f_m(player->velocity, dt); - new_p = glmth_v2_a(player->pos.local, player_delta); + player_delta = math_v2f_m(player->velocity, dt); + new_p = math_v2_a(player->pos.local, player_delta); remaining_time_factor -= smallest_distance_scale_factor; } } @@ -685,9 +686,9 @@ void game_update_and_render(struct GameMemory *game_memory, struct GameInput *ga shader_use(&game_state->renderer.shader); struct Entity player = game_state->player; - m4 model = glmth_m4_init_id(); - model = glmth_translate(model, (v3) {player.pos.local.x, player.pos.local.y, 0.0f}); - model = glmth_scale(model, (v3) {player.dimensions.x, player.dimensions.y, 1.0f}); + m4 model = math_m4_init_id(); + model = math_translate(model, (v3) {player.pos.local.x, player.pos.local.y, 0.0f}); + model = math_scale(model, (v3) {player.dimensions.x, player.dimensions.y, 1.0f}); v3 color = (v3) { 1.0f, 0.0f, 1.0f }; @@ -725,10 +726,10 @@ internal void render_debug_quad(struct GameState *game_state, rect r, v3 color, v2 dim = {r.max.x - r.min.x, r.max.y - r.min.y}; - m4 model = glmth_m4_init_id(); - model = glmth_translate(model, (v3) {dim.x * 0.5f, dim.y * 0.5f, 0.0f}); - model = glmth_translate(model, (v3) {r.min.x, r.min.y, 0.0f}); - model = glmth_scale(model, (v3) {dim.width, dim.height, 1.0f}); + m4 model = math_m4_init_id(); + model = math_translate(model, (v3) {dim.x * 0.5f, dim.y * 0.5f, 0.0f}); + model = math_translate(model, (v3) {r.min.x, r.min.y, 0.0f}); + model = math_scale(model, (v3) {dim.width, dim.height, 1.0f}); shader_setv3(&game_state->renderer.shader, "color", &color); shader_setm4(&game_state->renderer.shader, "model", &model); diff --git a/src/game.h b/src/game.h @@ -8,7 +8,7 @@ #include <glad/glad.h> #include "types.h" -#include "glmth.h" +#include "am_math.h" #include "shader.h" #include "platform.h" diff --git a/src/glmth.h b/src/glmth.h @@ -1,625 +0,0 @@ -#ifndef M_PI -#define M_PI 3.14159265359f -#endif - -// TODO: make sure these functions are inlineable and that the compiler does -// indeed inline them. - -internal inline f32 glmth_min(f32 a, f32 b) -{ - f32 min = a < b ? a : b; - return min; -} - -internal inline f32 glmth_max(f32 a, f32 b) -{ - f32 max = a > b ? a : b; - return max; -} - -internal inline i32 glmth_round(f32 n) -{ - return n < 0.0f ? n - 0.5 : n + 0.5; -} - -internal inline f32 glmth_wrap(f32 n, f32 min, f32 max) -{ - if (n > max) - { - return min; - } - else if (n < min) - { - return max; - } - else - { - return n; - } -} - -internal inline f32 glmth_rand(f32 min, f32 max) -{ - assert(min < max); - f32 random = ((f32) rand()) / (f32) RAND_MAX; - f32 diff = max - min; - f32 r = random * diff; - return min + r; -} - -internal inline m4 glmth_m4_init_id() -{ - m4 m = {0}; - m.E[0][0] = 1.0f; - m.E[1][1] = 1.0f; - m.E[2][2] = 1.0f; - m.E[3][3] = 1.0f; - return m; -} - -internal inline void glmth_m4_valueptr(m4 m, f32* out_valueptr) -{ - for (u8 v = 0; v < 16; ++v) - { - u8 row = v / 4; - u8 col = v % 4; - out_valueptr[v] = m.E[row][col]; - } -} - -internal inline bool glmth_m4m4_eq(m4 mat1, m4 mat2) -{ - for (u8 i = 0; i < 4; ++i) - { - for (u8 j = 0; j < 4; ++j) - { - if (mat1.E[i][j] != mat2.E[i][j]) - { - return false; - } - } - } - return true; -} - -internal inline m4 glmth_m4m4_m(m4 mat1, m4 mat2) -{ - m4 r = { - .E[0][0] = (mat1.E[0][0] * mat2.E[0][0]) + (mat1.E[0][1] * mat2.E[1][0]) + (mat1.E[0][2] * mat2.E[2][0]) + (mat1.E[0][3] * mat2.E[3][0]), - .E[0][1] = (mat1.E[0][0] * mat2.E[0][1]) + (mat1.E[0][1] * mat2.E[1][1]) + (mat1.E[0][2] * mat2.E[2][1]) + (mat1.E[0][3] * mat2.E[3][1]), - .E[0][2] = (mat1.E[0][0] * mat2.E[0][2]) + (mat1.E[0][1] * mat2.E[1][2]) + (mat1.E[0][2] * mat2.E[2][2]) + (mat1.E[0][3] * mat2.E[3][2]), - .E[0][3] = (mat1.E[0][0] * mat2.E[0][3]) + (mat1.E[0][1] * mat2.E[1][3]) + (mat1.E[0][2] * mat2.E[2][3]) + (mat1.E[0][3] * mat2.E[3][3]), - - .E[1][0] = (mat1.E[1][0] * mat2.E[0][0]) + (mat1.E[1][1] * mat2.E[1][0]) + (mat1.E[1][2] * mat2.E[2][0]) + (mat1.E[1][3] * mat2.E[3][0]), - .E[1][1] = (mat1.E[1][0] * mat2.E[0][1]) + (mat1.E[1][1] * mat2.E[1][1]) + (mat1.E[1][2] * mat2.E[2][1]) + (mat1.E[1][3] * mat2.E[3][1]), - .E[1][2] = (mat1.E[1][0] * mat2.E[0][2]) + (mat1.E[1][1] * mat2.E[1][2]) + (mat1.E[1][2] * mat2.E[2][2]) + (mat1.E[1][3] * mat2.E[3][2]), - .E[1][3] = (mat1.E[1][0] * mat2.E[0][3]) + (mat1.E[1][1] * mat2.E[1][3]) + (mat1.E[1][2] * mat2.E[2][3]) + (mat1.E[1][3] * mat2.E[3][3]), - - .E[2][0] = (mat1.E[2][0] * mat2.E[0][0]) + (mat1.E[2][1] * mat2.E[1][0]) + (mat1.E[2][2] * mat2.E[2][0]) + (mat1.E[2][3] * mat2.E[3][0]), - .E[2][1] = (mat1.E[2][0] * mat2.E[0][1]) + (mat1.E[2][1] * mat2.E[1][1]) + (mat1.E[2][2] * mat2.E[2][1]) + (mat1.E[2][3] * mat2.E[3][1]), - .E[2][2] = (mat1.E[2][0] * mat2.E[0][2]) + (mat1.E[2][1] * mat2.E[1][2]) + (mat1.E[2][2] * mat2.E[2][2]) + (mat1.E[2][3] * mat2.E[3][2]), - .E[2][3] = (mat1.E[2][0] * mat2.E[0][3]) + (mat1.E[2][1] * mat2.E[1][3]) + (mat1.E[2][2] * mat2.E[2][3]) + (mat1.E[2][3] * mat2.E[3][3]), - - .E[3][0] = (mat1.E[3][0] * mat2.E[0][0]) + (mat1.E[3][1] * mat2.E[1][0]) + (mat1.E[3][2] * mat2.E[2][0]) + (mat1.E[3][3] * mat2.E[3][0]), - .E[3][1] = (mat1.E[3][0] * mat2.E[0][1]) + (mat1.E[3][1] * mat2.E[1][1]) + (mat1.E[3][2] * mat2.E[2][1]) + (mat1.E[3][3] * mat2.E[3][1]), - .E[3][2] = (mat1.E[3][0] * mat2.E[0][2]) + (mat1.E[3][1] * mat2.E[1][2]) + (mat1.E[3][2] * mat2.E[2][2]) + (mat1.E[3][3] * mat2.E[3][2]), - .E[3][3] = (mat1.E[3][0] * mat2.E[0][3]) + (mat1.E[3][1] * mat2.E[1][3]) + (mat1.E[3][2] * mat2.E[2][3]) + (mat1.E[3][3] * mat2.E[3][3]), - }; - return r; -} - -internal inline v4 glmth_m4v4_m(m4 m, v4 v) -{ - v4 r = { - .x = (m.E[0][0] * v.x) + (m.E[0][1] * v.y) + (m.E[0][2] * v.z) + (m.E[0][3] * v.w), - .y = (m.E[1][0] * v.x) + (m.E[1][1] * v.y) + (m.E[1][2] * v.z) + (m.E[1][3] * v.w), - .z = (m.E[2][0] * v.x) + (m.E[2][1] * v.y) + (m.E[2][2] * v.z) + (m.E[2][3] * v.w), - .w = (m.E[3][0] * v.x) + (m.E[3][1] * v.y) + (m.E[3][2] * v.z) + (m.E[3][3] * v.w), - }; - return r; -} - -internal inline bool glmth_approx(f32 n, f32 compare, f32 epsilon) -{ - return fabsf(n - compare) <= epsilon; -} - -internal inline bool glmth_v2_is_nonzero(v2 v) -{ - // TODO: Figure out whether this epsilon is universally appropriate. - f32 epsilon = 0.000001; - return ( - !glmth_approx(v.x, 0.0f, epsilon) - || !glmth_approx(v.y, 0.0f, epsilon) - ); -} - -internal inline v2 glmth_v2_a(v2 vec1, v2 vec2) -{ - v2 r = { - .x = vec1.x + vec2.x, - .y = vec1.y + vec2.y, - }; - return r; -} - -internal inline v2 glmth_v2_negate(v2 v) -{ - v2 r = {-v.x, -v.y}; - return r; -} - -internal inline v2 glmth_v2_s(v2 vec1, v2 vec2) -{ - return glmth_v2_a(vec1, glmth_v2_negate(vec2)); -} - -internal inline v2 glmth_v2f_m(v2 v, f32 s) -{ - v2 r = {v.x * s, v.y * s}; - return r; -} - -internal inline f32 glmth_v2_length(v2 v) -{ - // TODO: Use inner product of v with itself - return sqrtf((v.x * v.x) + (v.y * v.y)); -} - -// TODO: handle degenerate case where l is near 0.0f -// TODO: scale by 1.0f / l -internal inline v2 glmth_v2_normalize(v2 v) -{ - f32 l = glmth_v2_length(v); - v2 r = {v.x / l, v.y / l}; - return r; -} - -internal inline f32 glmth_v2_dot(v2 vec1, v2 vec2) -{ - // dot product, a.k.a. inner product or scalar product - f32 dot_product = (vec1.x * vec2.x) + (vec1.y * vec2.y); - // NOTE: this value happens to be: - // |vec1| * |vec2| * Cos(theta) - // where theta is the angle between vec1 and vec2. This is useful in all - // sorts of ways: What if theta is zero? What if vec1 or vec2 is a unit - // vector? - return dot_product; -} - -internal inline v2i glmth_v2i_a(v2i vec1, v2i vec2) -{ - v2i r = {vec1.x + vec2.x, vec1.y + vec2.y}; - return r; -} - -internal inline v3 glmth_v3_cross(v3 vec1, v3 vec2) -{ - // cross product, a.k.a. outer product or vector product - v3 r = { - .x = (vec1.y * vec2.z) - (vec1.z * vec2.y), - .y = (vec1.z * vec2.x) - (vec1.x * vec2.z), - .z = (vec1.x * vec2.y) - (vec1.y * vec2.x), - }; - return r; -} - -internal inline f32 glmth_v3_length(v3 v) -{ - // TODO: Use inner product of v with itself - // NOTE: glmth_v3_dot(v, v) returns: - // |v| * |v| * Cos(theta) - // We know that theta, the angle between v and itself, is always 0, so we - // know that Cos(theta) is always 1. We can then simplify the above - // expression to be: - // (|v|)^2 - // so sqrtf(glmth_v3_dot(v, v)) is the length of v - return sqrtf((v.x * v.x) + (v.y * v.y) + (v.z * v.z)); -} - -internal inline v3 glmth_v3f_m(v3 v, f32 s) -{ - v3 r = {.x = v.x * s, .y = v.y * s, .z = v.z * s}; - return r; -} - -internal inline v3 glmth_v3_negate(v3 v) -{ - v3 r = {.x = -v.x, .y = -v.y, .z = -v.z}; - return r; -} - -// TODO: handle degenerate case where l is near 0.0f -// TODO: scale by 1.0f / l -internal inline v3 glmth_v3_normalize(v3 v) -{ - f32 l = glmth_v3_length(v); - v3 r = {v.x / l, v.y / l, v.z / l}; - return r; -} - -internal inline v3 glmth_v3_a(v3 vec1, v3 vec2) -{ - v3 r = { - .x = vec1.x + vec2.x, - .y = vec1.y + vec2.y, - .z = vec1.z + vec2.z - }; - return r; -} - -internal inline v3 glmth_v3_s(v3 vec1, v3 vec2) -{ - return glmth_v3_a(vec1, glmth_v3_negate(vec2)); -} - -internal inline bool glmth_v4v4_eq(v4 vec1, v4 vec2) -{ - for (u8 i = 0; i < 4; ++i) - { - if (vec1.E[i] != vec2.E[i]) - { - return false; - } - } - return true; -} - -internal inline void glmth_clamp(f32 *f, f32 min, f32 max) -{ - if (*f < min) - { - *f = min; - } - else if (*f > max) - { - *f = max; - } -} - -internal inline f32 glmth_deg(f32 rad) -{ - return rad * (180.0f / M_PI); -} - -internal inline f32 glmth_lerpf(f32 f, f32 min, f32 max) -{ - assert(f >= 0.0f && f <= 1.0f); - return (1.0f - f) * min + f * max; -} - -internal inline f32 glmth_rad(f32 deg) -{ - return deg * (M_PI / 180.0f); -} - -internal inline m4 glmth_rotate_x(m4 m, f32 rad) -{ - f32 c = cosf(rad); - f32 s = sinf(rad); - - m4 r = glmth_m4_init_id(); - - r.E[1][1] = c; - r.E[1][2] = -s; - - r.E[2][1] = s; - r.E[2][2] = c; - - return glmth_m4m4_m(m, r); -} - -internal inline m4 glmth_rotate_y(m4 m, f32 rad) -{ - f32 c = cosf(rad); - f32 s = sinf(rad); - - m4 r = glmth_m4_init_id(); - - r.E[0][0] = c; - r.E[0][2] = s; - - r.E[2][0] = -s; - r.E[2][2] = c; - - return glmth_m4m4_m(m, r); -} - -internal inline m4 glmth_rotate_z(m4 m, f32 rad) -{ - f32 c = cosf(rad); - f32 s = sinf(rad); - - m4 r = glmth_m4_init_id(); - - r.E[0][0] = c; - r.E[0][1] = -s; - - r.E[1][0] = s; - r.E[1][1] = c; - - return glmth_m4m4_m(m, r); -} - -internal inline m4 glmth_rotate(m4 m, f32 rad, v3 axis) -{ - axis = glmth_v3_normalize(axis); - - f32 c = cosf(rad); - f32 s = sinf(rad); - - m4 r = glmth_m4_init_id(); - - r.E[0][0] = c + (powf(axis.x, 2.0f) * (1 - c)); - r.E[0][1] = (axis.x * axis.y * (1 - c)) - (axis.z * s); - r.E[0][2] = (axis.x * axis.z * (1 - c)) + (axis.y * s); - - r.E[1][0] = (axis.y * axis.x * (1 - c)) + (axis.z * s); - r.E[1][1] = c + (powf(axis.y, 2.0f) * (1 - c)); - r.E[1][2] = (axis.y * axis.z * (1 - c)) - (axis.x * s); - - r.E[2][0] = (axis.z * axis.x * (1 - c)) - (axis.y * s); - r.E[2][1] = (axis.z * axis.y * (1 - c)) + (axis.x * s); - r.E[2][2] = c + (powf(axis.z, 2.0f) * (1 - c)); - - return glmth_m4m4_m(m, r); -} - -internal inline m4 glmth_scale(m4 m, v3 v) -{ - m4 r = glmth_m4_init_id(); - r.E[0][0] = v.x; - r.E[1][1] = v.y; - r.E[2][2] = v.z; - return glmth_m4m4_m(m, r); -} - -internal inline m4 glmth_translate(m4 m, v3 v) -{ - m4 r = glmth_m4_init_id(); - r.E[0][3] = v.x; - r.E[1][3] = v.y; - r.E[2][3] = v.z; - return glmth_m4m4_m(m, r); -} - -internal inline m4 glmth_projection_ortho(f32 left, f32 right, f32 bottom, f32 top, f32 near, f32 far) -{ - // TODO: This assert fails when you minimise the window in Windows. - assert(left != right); - assert(bottom != top); - assert(near != far); - - m4 r = glmth_m4_init_id(); - - r.E[0][0] = 2.0f / (right - left); - r.E[0][1] = 0.0f; - r.E[0][2] = 0.0f; - r.E[0][3] = -(right + left) / (right - left); - - r.E[1][0] = 0.0f; - r.E[1][1] = 2.0f / (top - bottom); - r.E[1][2] = 0.0f; - r.E[1][3] = -(top + bottom) / (top - bottom); - - r.E[2][0] = 0.0f; - r.E[2][1] = 0.0f; - r.E[2][2] = -2.0f / (far - near); - r.E[2][3] = -(far + near) / (far - near); - - r.E[3][0] = 0.0f; - r.E[3][1] = 0.0f; - r.E[3][2] = 0.0f; - r.E[3][3] = 1.0f; - - return r; -} - -internal inline m4 glmth_projection_perspective(f32 left, f32 right, f32 bottom, f32 top, f32 near, f32 far) -{ - assert(left != right); - assert(bottom != top); - assert(near != far); - - m4 r = glmth_m4_init_id(); - - r.E[0][0] = (2.0f * near) / (right - left); - r.E[0][1] = 0.0f; - r.E[0][2] = (right + left) / (right - left); - r.E[0][3] = 0.0f; - - r.E[1][0] = 0.0f; - r.E[1][1] = (2.0f * near) / (top - bottom); - r.E[1][2] = (top + bottom) / (top - bottom); - r.E[1][3] = 0.0f; - - r.E[2][0] = 0.0f; - r.E[2][1] = 0.0f; - r.E[2][2] = -(far + near) / (far - near); - r.E[2][3] = (-2.0f * far * near) / (far - near); - - r.E[3][0] = 0.0f; - r.E[3][1] = 0.0f; - r.E[3][2] = -1.0f; - r.E[3][3] = 0.0f; - - return r; -} - -internal inline m4 glmth_projection_perspective_fov(f32 fovy, f32 aspect, f32 near, f32 far) -{ - f32 half_height = tanf(fovy / 2.0f) * near; - f32 half_width = half_height * aspect; - f32 left = -half_width; - f32 right = half_width; - f32 bottom = -half_height; - f32 top = half_height; - - return glmth_projection_perspective(left, right, bottom, top, near, far); -} - -internal inline m4 glmth_camera_look_at(v3 camera_pos, v3 camera_target, v3 up) -{ - v3 camera_direction = glmth_v3_normalize(glmth_v3_s(camera_pos, camera_target)); - v3 camera_right = glmth_v3_normalize(glmth_v3_cross(up, camera_direction)); - v3 camera_up = glmth_v3_cross(camera_direction, camera_right); - - m4 look = glmth_m4_init_id(); - look.E[0][0] = camera_right.x; - look.E[0][1] = camera_right.y; - look.E[0][2] = camera_right.z; - - look.E[1][0] = camera_up.x; - look.E[1][1] = camera_up.y; - look.E[1][2] = camera_up.z; - - look.E[2][0] = camera_direction.x; - look.E[2][1] = camera_direction.y; - look.E[2][2] = camera_direction.z; - - return glmth_m4m4_m(look, glmth_translate(glmth_m4_init_id(), glmth_v3_negate(camera_pos))); -} - -internal inline bool glmth_intersect_aabb_aabb(rect r1, rect r2) -{ - f32 total_w = (r1.max.x - r1.min.x) + (r2.max.x - r2.min.x); - f32 total_h = (r1.max.y - r1.min.y) + (r2.max.y - r2.min.y); - bool aabbs_do_indeed_intersect = ( - fabsf(r1.max.x - r2.min.x) <= total_w - && fabsf(r1.max.y - r2.min.y) <= total_h - && fabsf(r2.max.x - r1.min.x) <= total_w - && fabsf(r2.max.y - r1.min.y) <= total_h - ); - return aabbs_do_indeed_intersect; -} - -internal inline rect glmth_minkowski_sum_rect_rect(rect r1, v2 r2_dimensions) -{ - f32 r2_half_w = 0.5f * r2_dimensions.width; - f32 r2_half_h = 0.5f * r2_dimensions.height; - rect sum = { - .min = {r1.min.x - r2_half_w, r1.min.y - r2_half_h}, - .max = {r1.max.x + r2_half_w, r1.max.y + r2_half_h}, - }; - return sum; -} - -enum GlmthRectEdge -{ - RECT_EDGE_BOTTOM, - RECT_EDGE_TOP, - RECT_EDGE_LEFT, - RECT_EDGE_RIGHT, -}; - -internal inline segment glmth_rect_get_edge(rect r, enum GlmthRectEdge e) -{ - v2 nw = {r.min.x, r.max.y}; - v2 ne = r.max; - v2 sw = r.min; - v2 se = {r.max.x, r.min.y}; - - segment edge = {0}; - switch(e) - { - case RECT_EDGE_BOTTOM: - edge = (segment) {sw, se}; - break; - case RECT_EDGE_TOP: - edge = (segment) {nw, ne}; - break; - case RECT_EDGE_LEFT: - edge = (segment) {sw, nw}; - break; - case RECT_EDGE_RIGHT: - edge = (segment) {se, ne}; - break; - default: - assert(false); - break; - } - return edge; -} - -internal inline bool glmth_in_interval_open(f32 n, f32 min, f32 max) -{ - assert(min <= max); - bool in_open_interval = false; - in_open_interval = (n >= min && n <= max); - return in_open_interval; -} - -internal inline bool glmth_v2_lt(v2 v, v2 compare) -{ - bool is_lt = false; - is_lt = (v.x < compare.x && v.y < compare.y); - return is_lt; -} - -internal inline bool glmth_v2_le(v2 v, v2 compare) -{ - bool is_le = false; - is_le = (v.x <= compare.x && v.y <= compare.y); - return is_le; -} - -internal inline bool glmth_v2_ge(v2 v, v2 compare) -{ - bool is_ge = false; - is_ge = (v.x >= compare.x && v.y >= compare.y); - return is_ge; -} - -internal inline void glmth_v2_print(v2 v) -{ - printf("( %f, %f )\n", v.x, v.y); -} - -internal inline void glmth_v2u_print(v2u v) -{ - printf("( %u, %u )\n", v.x, v.y); -} - -internal inline void glmth_v2i_print(v2i v) -{ - printf("( %i, %i )\n", v.x, v.y); -} - -internal inline void glmth_v3_print(v3 v) -{ - printf("( %f, %f, %f )\n", v.x, v.y, v.z); -} - -internal inline void glmth_v4_print(v4 v) -{ - printf("( %f, %f, %f, %f )\n", v.x, v.y, v.z, v.w); -} - -internal inline void glmth_rect_print(rect r) -{ - printf("rect:\n"); - printf(" min: "); - glmth_v2_print(r.min); - printf(" max: "); - glmth_v2_print(r.max); -} - -internal inline void glmth_m4_print(m4 m) -{ - printf("[\n"); - printf(" %f, %f, %f, %f\n", m.E[0][0], m.E[0][1], m.E[0][2], m.E[0][3]); - printf(" %f, %f, %f, %f\n", m.E[1][0], m.E[1][1], m.E[1][2], m.E[1][3]); - printf(" %f, %f, %f, %f\n", m.E[2][0], m.E[2][1], m.E[2][2], m.E[2][3]); - printf(" %f, %f, %f, %f\n", m.E[3][0], m.E[3][1], m.E[3][2], m.E[3][3]); - printf("]\n"); -} - -#define glmth_print(x) _Generic((x),\ - v2: glmth_v2_print, \ - v2u: glmth_v2u_print, \ - v2i: glmth_v2i_print, \ - v3: glmth_v3_print, \ - v4: glmth_v4_print, \ - rect: glmth_rect_print, \ - m4: glmth_m4_print \ - )(x) diff --git a/src/shader.c b/src/shader.c @@ -113,7 +113,7 @@ internal void shader_setm4(struct Shader *s, char *name, m4 *mat) if (s) { f32 valueptr[sizeof(m4)]; - glmth_m4_valueptr(*mat, valueptr); + math_m4_valueptr(*mat, valueptr); glUniformMatrix4fv(glGetUniformLocation(s->program, name), 1, GL_TRUE, valueptr); } else