#ifndef TWN_VEC_H #define TWN_VEC_H #include #include /* a point in some space (integer) */ typedef struct vec2 { _Alignas(8) int32_t x; int32_t y; } t_vec2; /* a point in some space (floating point) */ typedef struct fvec2 { _Alignas(8) float x; float y; } t_fvec2; /* a point in some three dimension space (floating point) */ /* y goes up, x goes to the right */ typedef struct fvec3 { _Alignas(16) float x; float y; float z; } t_fvec3; /* a point in some three dimension space (floating point) */ /* y goes up, x goes to the right */ typedef struct fvec4 { _Alignas(16) float x; float y; float z; float w; } t_fvec4; /* a point in some space (short) */ typedef struct shvec2 { _Alignas(4) int16_t x; int16_t y; } t_shvec2; /* aren't macros to prevent double evaluation with side effects */ /* maybe could be inlined? i hope LTO will resolve this */ static inline t_fvec2 fvec2_from_vec2(t_vec2 vec) { return (t_fvec2) { .x = (float)vec.x, .y = (float)vec.y, }; } static inline t_fvec2 fvec2_from_shvec2(t_shvec2 vec) { return (t_fvec2) { .x = (float)vec.x, .y = (float)vec.y, }; } static inline t_fvec3 fvec3_add(t_fvec3 a, t_fvec3 b) { return (t_fvec3) { a.x + b.x, a.y + b.y, a.z + b.z }; } static inline t_fvec3 fvec3_sub(t_fvec3 a, t_fvec3 b) { return (t_fvec3) { a.x - b.x, a.y - b.y, a.z - b.z }; } static inline t_fvec2 fvec2_div(t_fvec2 a, t_fvec2 b) { return (t_fvec2) { a.x / b.x, a.y / b.y }; } static inline t_fvec2 fvec2_scale(t_fvec2 a, float s) { return (t_fvec2) { a.x * s, a.y * s }; } static inline t_fvec3 fvec3_scale(t_fvec3 a, float s) { return (t_fvec3) { a.x * s, a.y * s, a.z * s }; } static inline float fvec3_dot(t_fvec3 a, t_fvec3 b) { return a.x * b.x + a.y * b.y + a.z * b.z; } static inline t_fvec3 fvec3_cross(t_fvec3 a, t_fvec3 b) { return (t_fvec3) { a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x, }; } /* TODO: fast_sqrt version? */ static inline t_fvec3 fvec3_norm(t_fvec3 a) { const float n = sqrtf(fvec3_dot(a, a)); /* TODO: do we need truncating over epsilon as cglm does? */ return fvec3_scale(a, 1.0f / n); } static inline t_fvec3 fvec3_rotate(t_fvec3 v, float angle, t_fvec3 axis) { /* from cglm */ t_fvec3 v1, v2, k; float c, s; c = cosf(angle); s = sinf(angle); k = fvec3_norm(axis); /* Right Hand, Rodrigues' rotation formula: v = v*cos(t) + (kxv)sin(t) + k*(k.v)(1 - cos(t)) */ v1 = fvec3_scale(v, c); v2 = fvec3_cross(k, v); v2 = fvec3_scale(v2, s); v1 = fvec3_add(v1, v2); v2 = fvec3_scale(k, fvec3_dot(k, v) * (1.0f - c)); v = fvec3_add(v1, v2); return v; } #define m_to_fvec2(p_any_vec2) (_Generic((p_any_vec2), \ t_vec2: fvec2_from_vec2, \ t_shvec2: fvec2_from_shvec2 \ )(p_any_vec2)) #define m_vec_sub(p_any_vec0, p_any_vec1) (_Generic((p_any_vec0), \ t_fvec3: fvec3_sub \ )(p_any_vec0, p_any_vec1)) #define m_vec_div(p_any_vec0, p_any_vec1) (_Generic((p_any_vec0), \ t_fvec2: fvec2_div \ )(p_any_vec0, p_any_vec1)) #define m_vec_scale(p_any_vec, p_any_scalar) (_Generic((p_any_vec), \ t_fvec2: fvec2_scale, \ t_fvec3: fvec3_scale \ )(p_any_vec, p_any_scalar)) #define m_vec_dot(p_any_vec0, p_any_vec1) (_Generic((p_any_vec0), \ t_fvec3: fvec3_dot \ )(p_any_vec0, p_any_vec1)) #define m_vec_cross(p_any_vec0, p_any_vec1) (_Generic((p_any_vec0), \ t_fvec3: fvec3_cross \ )(p_any_vec0, p_any_vec1)) #define m_vec_norm(p_any_vec) (_Generic((p_any_vec), \ t_fvec3: fvec3_norm \ )(p_any_vec)) #endif