transparent-cube

Minimal cross-platform native/wasm graphics example.
git clone git://git.amin.space/transparent-cube.git
Log | Files | Refs | README | LICENSE

glmth.h (9340B)


      1 #pragma once
      2 
      3 #ifndef M_PI
      4 #define M_PI 3.14159265359f
      5 #endif
      6 
      7 typedef unsigned char      u8;
      8 typedef unsigned short     u16;
      9 typedef unsigned int       u32;
     10 typedef unsigned long long u64;
     11 typedef signed char        i8;
     12 typedef signed short       i16;
     13 typedef signed int         i32;
     14 typedef signed long long   i64;
     15 typedef float              f32;
     16 typedef double             f64;
     17 
     18 #define GLMTH_PI        3.14159265358979323846f
     19 #define GLMTH_M_2_PI    6.28318530717958647693f
     20 #define GLMTH_D_4_PI    1.27323954473516268615f
     21 #define GLMTH_D_PI_2    1.57079632679489661923f
     22 #define GLMTH_D_4_SQ_PI 0.40528473456935108578f
     23 
     24 static f32 glmth_floorf(f32 x)
     25 {
     26     f32 result = (f32)((i32)x - (x < 0.0f));
     27     return result;
     28 }
     29 
     30 // These Sine and Cosine approximations use a parabola adjusted to minimize
     31 // error. This lovely approximation was derived by "Nick" on the devmaster.net
     32 // forums:
     33 //
     34 // https://web.archive.org/web/20080228213915/http://www.devmaster.net/forums/showthread.php?t=5784
     35 static f32 glmth_sinf(f32 x)
     36 {
     37 
     38     // wrap to range [0, 2PI]
     39     f32 full_circles = glmth_floorf(x / GLMTH_M_2_PI);
     40     f32 full_circle_radians = full_circles * GLMTH_M_2_PI;
     41     x = x - full_circle_radians;
     42 
     43     // TODO: remove branches
     44     // wrap to range [-PI, PI]
     45     if(x < -GLMTH_PI)
     46     {
     47         x += GLMTH_M_2_PI;
     48     }
     49     else if(x > GLMTH_PI)
     50     {
     51         x -= GLMTH_M_2_PI;
     52     }
     53 
     54     // fit parabola to sine
     55     f32 f = 0.0f;
     56     f32 g = 0.0f;
     57     if(x < 0.0f)
     58     {
     59         f = (GLMTH_D_4_PI * x) + (GLMTH_D_4_SQ_PI * x * x);
     60         g = f * -f;
     61 
     62     }
     63     else
     64     {
     65         f = (GLMTH_D_4_PI * x) - (GLMTH_D_4_SQ_PI * x * x);
     66         g = f * f;
     67     }
     68 
     69     f32 h = g - f;
     70     f32 i = h * 0.225f;
     71     f = i + f;
     72 
     73     return f;
     74 }
     75 
     76 static f32 glmth_cosf(f32 x)
     77 {
     78     // wrap to range [0, 2PI]
     79     f32 full_circles = glmth_floorf(x / GLMTH_M_2_PI);
     80     f32 full_circle_radians = full_circles * GLMTH_M_2_PI;
     81     x = x - full_circle_radians;
     82 
     83     // TODO: remove branches
     84     // wrap to range [-PI, PI]
     85     if(x < -GLMTH_PI)
     86     {
     87         x += GLMTH_M_2_PI;
     88     }
     89     else if(x > GLMTH_PI)
     90     {
     91         x -= GLMTH_M_2_PI;
     92     }
     93 
     94     x += GLMTH_D_PI_2;
     95     if(x > GLMTH_PI)
     96     {
     97         x -= GLMTH_M_2_PI;
     98     }
     99 
    100     // fit parabola to cosine
    101     f32 f = 0.0f;
    102     f32 g = 0.0f;
    103     if(x < 0.0f)
    104     {
    105         f = (GLMTH_D_4_PI * x) + (GLMTH_D_4_SQ_PI * x * x);
    106         g = f * -f;
    107 
    108     }
    109     else
    110     {
    111         f = (GLMTH_D_4_PI * x) - (GLMTH_D_4_SQ_PI * x * x);
    112         g = f * f;
    113     }
    114 
    115     f32 h = g - f;
    116     f32 i = h * 0.225f;
    117     f = i + f;
    118     return f;
    119 }
    120 
    121 static f32 glmth_tanf(f32 x)
    122 {
    123     // TODO: make a proper approximation
    124     return glmth_sinf(x)/glmth_cosf(x);
    125 }
    126 
    127 typedef union
    128 {
    129     struct
    130     {
    131         f32 x, y;
    132     };
    133     f32 E[2];
    134 } v2;
    135 
    136 typedef union
    137 {
    138     struct
    139     {
    140         f32 x, y, z;
    141     };
    142     struct
    143     {
    144         v2 xy;
    145         f32 ignored_z;
    146     };
    147     struct
    148     {
    149         f32 ignored_x;
    150         v2 yz;
    151     };
    152     f32 E[3];
    153 } v3;
    154 
    155 typedef union
    156 {
    157     struct
    158     {
    159         union
    160         {
    161             v3 xyz;
    162             struct
    163             {
    164                 f32 x, y, z;
    165             };
    166         };
    167         f32 w;
    168     };
    169     struct
    170     {
    171         v2 xy;
    172         f32 ignored_z_xy;
    173         f32 ignored_w_xy;
    174     };
    175     struct
    176     {
    177         f32 ignored_x_yz;
    178         v2 yz;
    179         f32 ignored_w_yz;
    180     };
    181     struct
    182     {
    183         f32 ignored_x_zw;
    184         f32 ignored_y_zw;
    185         v2 zw;
    186     };
    187     f32 E[4];
    188 } v4;
    189 
    190 typedef struct
    191 {
    192     // row-major, so you probably want to transpose before passing to opengl,
    193     // which uses column-major matrices
    194     f32 E[4][4]; // E[row][column]
    195 } m4;
    196 
    197 static m4 glmth_m4_init_id(void)
    198 {
    199     m4 m = { 0 };
    200     m.E[0][0] = 1.0f;
    201     m.E[1][1] = 1.0f;
    202     m.E[2][2] = 1.0f;
    203     m.E[3][3] = 1.0f;
    204     return m;
    205 }
    206 
    207 static void glmth_m4_valueptr(m4 m, f32* out_valueptr)
    208 {
    209     for (u8 v = 0; v < 16; ++v)
    210     {
    211         u8 row = v / 4;
    212         u8 col = v % 4;
    213         out_valueptr[v] = m.E[row][col];
    214     }
    215 }
    216 
    217 static m4 glmth_m4m4_m(m4 mat1, m4 mat2)
    218 {
    219     m4 r = {
    220         .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]),
    221         .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]),
    222         .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]),
    223         .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]),
    224 
    225         .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]),
    226         .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]),
    227         .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]),
    228         .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]),
    229 
    230         .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]),
    231         .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]),
    232         .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]),
    233         .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]),
    234 
    235         .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]),
    236         .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]),
    237         .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]),
    238         .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]),
    239     };
    240     return r;
    241 }
    242 
    243 static v3 glmth_v3_init(f32 x, f32 y, f32 z)
    244 {
    245     v3 v = { .x = x, .y = y, .z = z };
    246     return v;
    247 }
    248 
    249 static f32 glmth_rad(f32 deg)
    250 {
    251     return deg * (M_PI / 180.0f);
    252 }
    253 
    254 static m4 glmth_rotate(m4 m, f32 rad, v3 axis)
    255 {
    256     f32 c = glmth_cosf(rad);
    257     f32 s = glmth_sinf(rad);
    258 
    259     m4 r = glmth_m4_init_id();
    260 
    261     r.E[0][0] = c + ((axis.x * axis.x) * (1 - c));
    262     r.E[0][1] = (axis.x * axis.y * (1 - c)) - (axis.z * s);
    263     r.E[0][2] = (axis.x * axis.z * (1 - c)) + (axis.y * s);
    264 
    265     r.E[1][0] = (axis.y * axis.x * (1 - c)) + (axis.z * s);
    266     r.E[1][1] = c + ((axis.y * axis.y) * (1 - c));
    267     r.E[1][2] = (axis.y * axis.z * (1 - c)) - (axis.x * s);
    268 
    269     r.E[2][0] = (axis.z * axis.x * (1 - c)) - (axis.y * s);
    270     r.E[2][1] = (axis.z * axis.y * (1 - c)) + (axis.x * s);
    271     r.E[2][2] = c + ((axis.z * axis.z) * (1 - c));
    272 
    273     return glmth_m4m4_m(m, r);
    274 }
    275 
    276 static m4 glmth_translate(m4 m, v3 v)
    277 {
    278     m4 r = glmth_m4_init_id();
    279     r.E[0][3] = v.x;
    280     r.E[1][3] = v.y;
    281     r.E[2][3] = v.z;
    282     return glmth_m4m4_m(m, r);
    283 }
    284 
    285 static m4 glmth_projection_ortho(f32 left, f32 right, f32 bottom, f32 top, f32 near, f32 far)
    286 {
    287     // TODO: This assert fails when you minimise the window in Windows.
    288     assert(left != right);
    289     assert(bottom != top);
    290     assert(near != far);
    291 
    292     m4 r = glmth_m4_init_id();
    293 
    294     r.E[0][0] = 2.0f / (right - left);
    295     r.E[0][1] = 0.0f;
    296     r.E[0][2] = 0.0f;
    297     r.E[0][3] = -(right + left) / (right - left);
    298 
    299     r.E[1][0] = 0.0f;
    300     r.E[1][1] = 2.0f / (top - bottom);
    301     r.E[1][2] = 0.0f;
    302     r.E[1][3] = -(top + bottom) / (top - bottom);
    303 
    304     r.E[2][0] = 0.0f;
    305     r.E[2][1] = 0.0f;
    306     r.E[2][2] = -2.0f / (far - near);
    307     r.E[2][3] = -(far + near) / (far - near);
    308 
    309     r.E[3][0] = 0.0f;
    310     r.E[3][1] = 0.0f;
    311     r.E[3][2] = 0.0f;
    312     r.E[3][3] = 1.0f;
    313 
    314     return r;
    315 }
    316 
    317 static m4 glmth_projection_perspective(f32 left, f32 right, f32 bottom, f32 top, f32 near, f32 far)
    318 {
    319     assert(left != right);
    320     assert(bottom != top);
    321     assert(near != far);
    322 
    323     m4 r = glmth_m4_init_id();
    324 
    325     r.E[0][0] = (2.0f * near) / (right - left);
    326     r.E[0][1] = 0.0f;
    327     r.E[0][2] = (right + left) / (right - left);
    328     r.E[0][3] = 0.0f;
    329 
    330     r.E[1][0] = 0.0f;
    331     r.E[1][1] = (2.0f * near) / (top - bottom);
    332     r.E[1][2] = (top + bottom) / (top - bottom);
    333     r.E[1][3] = 0.0f;
    334 
    335     r.E[2][0] = 0.0f;
    336     r.E[2][1] = 0.0f;
    337     r.E[2][2] = -(far + near) / (far - near);
    338     r.E[2][3] = (-2.0f * far * near) / (far - near);
    339 
    340     r.E[3][0] = 0.0f;
    341     r.E[3][1] = 0.0f;
    342     r.E[3][2] = -1.0f;
    343     r.E[3][3] = 0.0f;
    344 
    345     return r;
    346 }
    347 
    348 static m4 glmth_projection_perspective_fov(f32 fovy, f32 aspect, f32 near, f32 far)
    349 {
    350     f32 half_height = glmth_tanf(fovy / 2.0f) * near;
    351     f32 half_width = half_height * aspect;
    352     f32 left = -half_width;
    353     f32 right = half_width;
    354     f32 bottom = -half_height;
    355     f32 top = half_height;
    356 
    357     return glmth_projection_perspective(left, right, bottom, top, near, far);
    358 }