hs

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | Submodules | LICENSE

hs_graphics.h (51107B)


      1 #ifndef HS_GRAPHICS_H_
      2 #define HS_GRAPHICS_H_
      3 
      4 #include <assert.h>
      5 #include <string.h>
      6 #include <stdlib.h>
      7 #include <stdbool.h>
      8 #include <stdint.h>
      9 #include <stdio.h>
     10 
     11 #ifdef WIN32
     12 #define OEMRESOURCE
     13 #include <windows.h>
     14 #endif
     15 
     16 #include "hs_math.h"
     17 #include "hs_data.h"
     18 #include "hs_util.h"
     19 
     20 #define STBI_NO_SIMD
     21 
     22 #ifndef NO_STBI
     23 #define STB_IMAGE_IMPLEMENTATION
     24 #include "external/stb_image.h"
     25 #endif
     26 
     27 #include "external/glad/glad.h"
     28 #include "external/glfw/glfw3.h"
     29 
     30 #ifdef HS_SFD
     31 #include "external/sfd/src/sfd.c"
     32 #endif
     33 
     34 #ifdef HS_NUKLEAR
     35 #include "hs_nuklear.h"
     36 #endif
     37 
     38 #define hs_key_init(glfw_key) &(hs_key){.key = glfw_key}
     39 
     40 typedef struct {
     41         uint32_t count, vbo, vao, ebo;
     42 } hs_vobj;
     43 
     44 typedef uint32_t hs_tex;
     45 
     46 typedef struct {
     47         uint32_t model, view, proj;
     48 } hs_coord;
     49 
     50 typedef struct {
     51         uint32_t p;
     52         hs_vobj vobj;
     53         hs_coord coord;
     54 } hs_shader_program;
     55 
     56 typedef struct {
     57         vec3 pos, up, front;
     58         float yaw, pitch, roll;
     59 } hs_camera;
     60 
     61 typedef struct {
     62         vec2 curr, goal;
     63         float zoom;
     64 } hs_camera2_smooth;
     65 
     66 typedef struct {
     67         vec2 pos, tex;
     68 } hs_tex_corner;
     69 
     70 typedef struct {
     71          hs_tex_corner c[6];
     72 } hs_tex_square;
     73 
     74 typedef struct {
     75         uint32_t width, height, tileset_width, tileset_height;
     76         float tile_width, tile_height;
     77         hs_shader_program sp;
     78         hs_tex tex;
     79         hs_tex_square* vertices;
     80 } hs_tilemap;
     81 
     82 typedef struct {
     83         uint32_t tileset_width, tileset_height;
     84         float tile_width, tile_height;
     85         hs_shader_program sp;
     86         hs_tex tex;
     87         hs_dynarr vertices;
     88 } hs_dyn_tilemap;
     89 
     90 // this is how anders tale rooms are stored
     91 // --LAYERS MUST BE AT LEAST 1--
     92 typedef struct {
     93         uint16_t width, height, layers;
     94         uint8_t* data;
     95 } hs_aroom;
     96 
     97 typedef struct {
     98         uint32_t width, height;
     99         GLFWwindow* window;
    100 } hs_game_data;
    101 
    102 typedef struct {
    103         vec2 tr, bl;
    104 } hs_aabb2;
    105 
    106 typedef struct {
    107         vec2i tr, bl;
    108 } hs_aabb2i;
    109 
    110 typedef struct {
    111         vec2 pos, half_size;
    112 } hs_rect2;
    113 
    114 typedef struct {
    115         hs_rect2 r;
    116         uint32_t flags;
    117         hs_tex tex;
    118         hs_shader_program sp;
    119 } hs_entity2_hot;
    120 
    121 typedef struct {
    122         vec2 external_velocity;
    123         float base_mov_speed, mov_speed_mul, fire_rate_mul, invisframe_mul;
    124         uint32_t max_hp, hp, armour;
    125         void* current_room; //TODO: create room struct and stuff
    126 } hs_entity2_cold;
    127 
    128 typedef struct {
    129         hs_entity2_hot* hot;
    130         hs_entity2_cold cold;
    131 } hs_entity2;
    132 
    133 enum entity2_flags {
    134         AABB_STATIC = 1 << 0,
    135         AABB_RIGID = 1 << 1,
    136         //AABB_CHARACTHER = 1 << 2,
    137         PLAYER = 1 << 3,
    138         PREBLINK = 1 << 4,
    139         INVISFRAME = 1 << 5,
    140         CONFUSION = 1 << 6,
    141         STUNNED = 1 << 7,
    142         DISABLED = 1 << 8,
    143 };
    144 
    145 enum hs_key_state {
    146         HS_KEY_UP = 0,
    147         HS_KEY_DOWN = 1,
    148         HS_KEY_RELEASED = 2,
    149         HS_KEY_PRESSED = 3,
    150 };
    151 
    152 typedef struct {
    153         const int key;
    154         int previous_state;
    155 } hs_key;
    156 
    157 /* Keys */
    158 extern enum hs_key_state hs_get_key_toggle(const hs_game_data gd,   hs_key* key);
    159 extern enum hs_key_state hs_get_mouse_toggle(const hs_game_data gd, hs_key* key);
    160 extern enum hs_key_state hs_get_key_held(const hs_game_data gd,   const int key);
    161 extern enum hs_key_state hs_get_mouse_held(const hs_game_data gd, const int key);
    162 
    163 /* MISC */
    164 extern void     hs_close(const hs_game_data gd);
    165 extern float    hs_delta();
    166 extern void     hs_sp_use(const hs_shader_program sp);
    167 extern int32_t  hs_window_up(const hs_game_data gd);
    168 extern void     hs_clear(const float r, const  float g, const  float b, const  float a, const GLbitfield mask);
    169 extern void     hs_avg_frametime_print(const float delta, const float interval);
    170 extern void     hs_avg_fps_print(const float delta, const float interval);
    171 extern void     hs_fps_callback_init(const hs_game_data gd, void(*mouse_callback)(GLFWwindow*, double xpos, double ypos));
    172 
    173 /* Vertex Buffer */
    174 extern uint32_t hs_vao_create(const uint32_t  count);
    175 extern uint32_t hs_vbo_create(const float    *vbuff, const uint32_t buffsize,
    176                               const GLenum    usage, const uint32_t count);
    177 extern uint32_t hs_ebo_create(const uint32_t *ibuff, const uint32_t buffsize,
    178                               const GLenum    usage, const uint32_t count);
    179 extern hs_vobj  hs_vobj_create(const float    *vbuff, const uint32_t vbuffsize,
    180                               const uint32_t *ibuff, const uint32_t ibuffsize,
    181                               const GLenum    usage, const uint32_t count);
    182 extern void     hs_vobj_free(hs_vobj vobj);
    183 extern void     hs_vattrib_enable(const uint32_t index, const uint32_t size, const GLenum type,
    184                                   const uint32_t stride, const size_t pointer);
    185 extern void     hs_vattrib_enable_float(const uint32_t index, const uint32_t size,
    186                                          const uint32_t stride, const size_t pointer);
    187 
    188 /* Shader program */
    189 extern hs_shader_program hs_shader_program_create(const uint32_t sp, hs_vobj vobj);
    190 extern uint32_t          hs_shader_create(const char *src, const GLenum shader_type);
    191 extern uint32_t          hs_sp_create(const uint32_t v_shader, const uint32_t f_shader);
    192 extern void              hs_sp_delete(hs_shader_program sp);
    193 extern uint32_t          hs_sp_texture_transform_create();
    194 extern uint32_t hs_sp_create_from_src(const char *v_src,   const char *f_src);
    195 extern uint32_t hs_sp_create_from_file(const char *v_file, const char *f_file);
    196 
    197 /* Uniforms */
    198 extern uint32_t hs_uniform_create(const uint32_t program, const char* name);
    199 extern hs_coord hs_uniform_coord_create(const uint32_t program, const char* model, const char* view, const char* proj);
    200 extern void     hs_uniform_mat4_set(const uint32_t u_mat, const mat4 mat);
    201 extern void     hs_uniform_sp_mat4_set(const uint32_t program, const uint32_t u_mat, const mat4 mat);
    202 extern void     hs_uniform_vec2_set(const uint32_t u_vec, const vec2 vec);
    203 extern void     hs_uniform_sp_vec2_set(const uint32_t program, const uint32_t u_vec, const vec2 vec);
    204 extern void     hs_tex_uniform_set(const hs_tex tex, const uint32_t val);
    205 extern void     hs_tex2d_activate(const uint32_t texture_object, const GLenum texindex);
    206 #ifndef NO_STBI
    207 extern uint32_t hs_tex2d_create(const char *filename, const GLenum format, const GLenum  wrap, const GLenum filter);
    208 extern uint32_t hs_tex2d_create_pixel(const char *filename, const GLenum format);
    209 extern uint32_t hs_tex2d_create_size_info(const char *filename, const GLenum format, const GLenum wrap, const GLenum filter, int* width, int* height);
    210 extern uint32_t hs_tex2d_create_size_info_pixel(const char *filename, const GLenum format, int* width, int* height);
    211 #endif
    212 
    213 /* FBO */
    214 extern uint32_t hs_fbo_color_create(const uint32_t width,  const uint32_t height, uint32_t* tex);
    215 extern void     hs_fbo_draw_to_screen(const uint32_t fbo,  const uint32_t src_w, const uint32_t src_h,
    216                       const uint32_t dst_x, const uint32_t dst_y, const uint32_t dst_w, const uint32_t dst_h);
    217 
    218 /* hs_aabb2 */
    219 extern vec2     hs_aabb2_center(const hs_aabb2 rect);
    220 extern vec2     hs_aabb2_size(const hs_aabb2 rect);
    221 extern vec2     hs_aabb2_half_size(const hs_aabb2 rect);
    222 extern vec2i    hs_aabb2i_center(const hs_aabb2i rect);
    223 extern vec2i    hs_aabb2i_size(const hs_aabb2i rect);
    224 extern vec2i    hs_aabb2i_half_size(const hs_aabb2i rect);
    225 extern hs_aabb2 hs_aabb2_from_rect2(const hs_rect2 r);
    226 
    227 /* Anders Tale Dungeon generation v3 (BSP) */
    228 extern uint32_t hs_bsp_recti_split_in_place_append(hs_aabb2i* rects, const uint32_t new_rect_index, const vec2i min_rect_size);
    229 
    230 /* Physics */
    231 extern uint32_t hs_rect2_is_inside(const hs_rect2 r1, const hs_rect2 r2, float* lenabs);
    232 extern void     hs_entity2_force_inside_rects(hs_rect2* e, hs_rect2* rects, const uint32_t rectc);
    233 extern void     hs_entity2_collide(hs_rect2* r1, const hs_rect2* r2);
    234 
    235 /* Camera */
    236 extern hs_camera hs_init_fps_camera();
    237 extern void      hs_camera_move_front(hs_camera* camera, const float scale);
    238 extern void      hs_camera_move_right(hs_camera* camera, const float scale);
    239 extern void      hs_camera_move_frontv(const hs_camera camera, vec3* new_pos);
    240 extern void      hs_camera_move_backv(const hs_camera camera, vec3* new_pos);
    241 extern void      hs_camera_move_rightv(const hs_camera camera, vec3* new_pos);
    242 extern void      hs_camera_move_leftv(const hs_camera camera, vec3* new_pos);
    243 extern void      hs_camera_look_at(mat4 view, const hs_camera camera);
    244 extern vec2      hs_pos_to_offset(const float xpos, const float ypos, const float sens);
    245 extern void      hs_camera_update_front(hs_camera* camera);
    246 
    247 #define HS_CAMERA2_SMOOTH_DEFAULT {.zoom = 1.0f}
    248 extern void hs_camera2_smooth_view(mat4 view, const hs_camera2_smooth cam);
    249 extern void hs_camera2_smooth_move_to_goal(hs_camera2_smooth* cam, const float scale);
    250 extern vec2 hs_px_coord_to_global(const vec2 cam_offset, const vec2 scale, const hs_aabb2 res, const vec2 px);
    251 
    252 /* Tilemap */
    253 extern hs_aroom hs_aroom_from_file(const char* file_path);
    254 extern void     hs_aroom_write_to_file(const char* file_path, const hs_aroom aroom);
    255 extern void     hs_tex_square_set_pos(hs_tex_square* square, const hs_aabb2 pos);
    256 extern void     hs_tex_square_set_tex(hs_tex_square* square, hs_aabb2 tex);
    257 
    258 extern void     hs_tilemap_set(hs_tilemap* tilemap, const uint32_t vertex, uint32_t tile);
    259 extern void     hs_tilemap_setall(hs_tilemap* tilemap, const uint32_t tile);
    260 extern void     hs_tilemap_set_xy(hs_tilemap* tilemap, const uint32_t x, const uint32_t y, uint32_t tile);
    261 extern uint32_t hs_tilemap_sizeof(const hs_tilemap tilemap);
    262 extern void     hs_tilemap_init(hs_tilemap* tilemap, const uint32_t default_tex);
    263 extern void     hs_tilemap_update_vbo(const hs_tilemap tilemap);
    264 extern void     hs_tilemap_draw(const hs_tilemap tilemap);
    265 extern void     hs_tilemap_free(hs_tilemap* tilemap);
    266 extern void     hs_aroom_set_xy(hs_aroom* aroom, const uint16_t x, const uint16_t y, const uint16_t data);
    267 extern void     hs_aroom_to_tilemap(const hs_aroom aroom, hs_tilemap* tilemap, const uint16_t layer);
    268 extern uint8_t  hs_aroom_get_xy(const hs_aroom aroom, const uint16_t x, const uint16_t y);
    269 extern void     hs_aroom_set_tilemap(const hs_aroom aroom, hs_tilemap* tilemap, const uint16_t layer);
    270 extern void     hs_aroom_set_tilemap_offsetv(const hs_aroom aroom, hs_tilemap* tilemap, const uint16_t layer, const vec2i offset);
    271 extern vec2     hs_tilemap_pos_to_global(const hs_tilemap tilemap, vec2i pos);
    272 
    273 extern uint32_t hs_dyn_tilemap_sizeof(const hs_dyn_tilemap tilemap);
    274 extern void     hs_dyn_tilemap_init(hs_dyn_tilemap* tilemap, size_t size);
    275 extern void     hs_dyn_tilemap_clear(hs_dyn_tilemap* tilemap);
    276 extern void     hs_dyn_tilemap_free(hs_dyn_tilemap* tilemap);
    277 extern void     hs_dyn_tilemap_push(hs_dyn_tilemap* tilemap, const vec2i pos, uint32_t tile);
    278 extern void     hs_aroom_push_dyn_tilemap(const hs_aroom aroom, hs_dyn_tilemap* tilemap, const uint16_t layer, const vec2i offset);
    279 extern void     hs_dyn_tilemap_update_vbo(const hs_dyn_tilemap tilemap);
    280 extern void     hs_dyn_tilemap_draw(const hs_dyn_tilemap tilemap);
    281 extern vec2     hs_dyn_tilemap_pos_to_global(const hs_dyn_tilemap tilemap, vec2i pos);
    282 
    283 /* Entity */
    284 extern hs_shader_program hs_sp_sprite_create(const float width, const float height, const float screen_size);
    285 extern void              hs_sprite_draw_current();
    286 
    287 extern hs_entity2 hs_entity2_create(hs_entity2_hot* hot, hs_shader_program sp, hs_tex tex);
    288 
    289 #ifdef HS_IMPL
    290 static uint32_t hs_default_missing_tex = 0;
    291 
    292 #define GLAD_IMPL
    293 #include "external/glad/glad_impl.h"
    294 #define GLFW_IMPL
    295 #include "external/glfw/glfw_impl.h"
    296 
    297 #define hs_loop(game_data, update_func) while(hs_window_up(game_data)) {hs_poll_input(); update_func; hs_end_frame(game_data);}
    298 
    299 inline void
    300 hs_clear(const float r, const  float g, const  float b, const  float a, const GLbitfield mask)
    301 {
    302         glClearColor(r, g, b, a);
    303         glClear(GL_COLOR_BUFFER_BIT | mask);
    304 }
    305 
    306 inline uint32_t
    307 hs_uniform_create(const uint32_t program, const char *name)
    308 {
    309         return glGetUniformLocation(program, name);
    310 }
    311 
    312 inline hs_coord
    313 hs_uniform_coord_create(const uint32_t program,
    314                         const char* model, const char* view, const char* proj)
    315 {
    316         const uint32_t u_model = glGetUniformLocation(program, model);
    317         const uint32_t u_view =  glGetUniformLocation(program, view);
    318         const uint32_t u_proj =  glGetUniformLocation(program, proj);
    319 
    320         return (hs_coord) {
    321                 .model = u_model,
    322                 .view  = u_view,
    323                 .proj  = u_proj,
    324         };
    325 }
    326 
    327 void
    328 hs_uniform_mat4_set(const uint32_t u_mat, const mat4 mat)
    329 {
    330         glUniformMatrix4fv(u_mat, 1, GL_FALSE, castf(mat));
    331 }
    332 
    333 void
    334 hs_uniform_sp_mat4_set(const uint32_t program, const uint32_t u_mat, const mat4 mat)
    335 {
    336         glUseProgram(program);
    337         glUniformMatrix4fv(u_mat, 1, GL_FALSE, castf(mat));
    338 }
    339 
    340 void
    341 hs_uniform_vec2_set(const uint32_t u_vec, const vec2 vec)
    342 {
    343         glUniform2fv(u_vec, 1, vec.xy);
    344 }
    345 
    346 void
    347 hs_uniform_sp_vec2_set(const uint32_t program, const uint32_t u_vec, const vec2 vec)
    348 {
    349         glUseProgram(program);
    350         glUniform2fv(u_vec, 1, vec.xy);
    351 }
    352 
    353 inline enum hs_key_state
    354 hs_get_key_toggle(const hs_game_data gd, hs_key* key)
    355 {
    356         const int state = glfwGetKey(gd.window, key->key);
    357         const int previous_state = key->previous_state;
    358         key->previous_state = state;
    359 
    360         if (previous_state != state) {
    361                 if (state == GLFW_PRESS) return HS_KEY_PRESSED;
    362                 else                     return HS_KEY_RELEASED;
    363         }
    364 
    365         return HS_KEY_UP;
    366 }
    367 
    368 inline enum hs_key_state
    369 hs_get_key_held(const hs_game_data gd, const int key)
    370 {
    371         const int state = glfwGetKey(gd.window, key);
    372         if (state == GLFW_PRESS)
    373                 return HS_KEY_DOWN;
    374         return HS_KEY_UP;
    375 }
    376 
    377 inline enum hs_key_state
    378 hs_get_mouse_toggle(const hs_game_data gd, hs_key* key)
    379 {
    380         const int state = glfwGetMouseButton(gd.window, key->key);
    381         const int previous_state = key->previous_state;
    382         key->previous_state = state;
    383 
    384         if (previous_state != state) {
    385                 if (state == GLFW_PRESS) return HS_KEY_PRESSED;
    386                 else                     return HS_KEY_RELEASED;
    387         }
    388 
    389         return HS_KEY_UP;
    390 }
    391 
    392 inline enum hs_key_state
    393 hs_get_mouse_held(const hs_game_data gd, const int key)
    394 {
    395         const int state = glfwGetMouseButton(gd.window, key);
    396         if (state == GLFW_PRESS)
    397                 return HS_KEY_DOWN;
    398         return HS_KEY_UP;
    399 }
    400 
    401 inline int
    402 hs_window_up(const hs_game_data gd)
    403 {
    404         return !glfwWindowShouldClose(gd.window);
    405 }
    406 
    407 inline void
    408 hs_close(const hs_game_data gd)
    409 {
    410         return glfwSetWindowShouldClose(gd.window, true);
    411 }
    412 
    413 inline void
    414 hs_vattrib_enable(const uint32_t index, const uint32_t size,
    415                   const GLenum type, const uint32_t stride, const size_t pointer)
    416 {
    417         glVertexAttribPointer(index, size, type, GL_FALSE, stride, (void*)pointer);
    418         glEnableVertexAttribArray(index);
    419 }
    420 
    421 inline void
    422 hs_vattrib_enable_float(const uint32_t index, const uint32_t size,
    423                         const uint32_t stride, const size_t pointer)
    424 {
    425         hs_vattrib_enable(index, size, GL_FLOAT, stride * sizeof(float), pointer * sizeof(float));
    426 }
    427 
    428 inline float
    429 hs_delta()
    430 {
    431         static float last_frame = 0.0f;
    432         float current_frame = glfwGetTime() * 1000.0f;
    433         float delta = current_frame - last_frame;
    434 
    435         last_frame = current_frame;
    436         return delta;
    437 }
    438 
    439 inline void
    440 hs_sp_use(const hs_shader_program sp)
    441 {
    442         glUseProgram(sp.p);
    443         glBindVertexArray(sp.vobj.vao);
    444         if (sp.vobj.ebo) glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, sp.vobj.ebo);
    445 }
    446 
    447 hs_aroom
    448 hs_aroom_from_file(const char* file_path)
    449 {
    450         uint8_t* data = hs_file_read(file_path);
    451         uint16_t* header = (uint16_t*)data;
    452         hs_aroom aroom;
    453         aroom.width = header[0];
    454         aroom.height = header[1];
    455         aroom.layers = header[2] == 0 ? 1 : header[2];
    456 
    457         uint8_t* aroom_data = (uint8_t*)malloc(aroom.width * aroom.height * aroom.layers);
    458         assert(aroom_data);
    459         memcpy(aroom_data, &data[sizeof(uint16_t)*3], aroom.width * aroom.height * aroom.layers);
    460         free(data);
    461 
    462         aroom.data = aroom_data;
    463         return aroom;
    464 }
    465 
    466 void
    467 hs_aroom_write_to_file(const char* file_path, const hs_aroom aroom)
    468 {
    469         uint16_t layers = aroom.layers == 0 ? 1 : aroom.layers;
    470         FILE *file = fopen(file_path, "wb");
    471         if (!file) {
    472                 fprintf(stderr, "---error writing to file \"%s\"---\n", file_path);
    473                 assert(file);
    474         }
    475 
    476         fwrite(&aroom.width, sizeof(uint16_t), 1, file);
    477         fwrite(&aroom.height, sizeof(uint16_t), 1, file);
    478         fwrite(&layers, sizeof(uint16_t), 1, file);
    479         fwrite(aroom.data, aroom.width * aroom.height * layers, 1, file);
    480         fclose(file);
    481 }
    482 
    483 uint32_t
    484 hs_shader_create(const char *src, const GLenum shader_type)
    485 {
    486         uint32_t shader = glCreateShader(shader_type);
    487         glShaderSource(shader, 1, &src, NULL);
    488         glCompileShader(shader);
    489 
    490         int  shader_compile_success;
    491         glGetShaderiv(shader, GL_COMPILE_STATUS, &shader_compile_success);
    492 
    493         if (!shader_compile_success) {
    494                 char info_log[512];
    495                 glGetShaderInfoLog(shader, 512, NULL, info_log);
    496                 fprintf(stderr, "-------------ERROR------------\n"
    497                        "::OpenGL Failed to compile shader::\n%s\n", info_log);
    498                 fprintf(stderr, "-------------SOURCE------------\n");
    499                 fprintf(stderr, "%s\n", src);
    500                 fprintf(stderr, "\n------------END_SOURCE----------\n");
    501                 assert(shader_compile_success);
    502         }
    503 
    504         return shader;
    505 }
    506 
    507 uint32_t
    508 hs_sp_create(const uint32_t v_shader, const uint32_t f_shader)
    509 {
    510         uint32_t program = glCreateProgram();
    511         glAttachShader(program, v_shader);
    512         glAttachShader(program, f_shader);
    513         glLinkProgram(program);
    514 
    515         int  program_link_success;
    516         glGetProgramiv(program, GL_LINK_STATUS, &program_link_success);
    517 
    518         if (!program_link_success) {
    519                 char info_log[512];
    520                 glGetProgramInfoLog(program, 512, NULL, info_log);
    521                 fprintf(stderr, "-------------ERROR------------\n"
    522                        "::OpenGL Failed to link program::\n%s\n", info_log);
    523                 assert(program_link_success);
    524         }
    525 
    526         glDeleteShader(v_shader);
    527         glDeleteShader(f_shader);
    528 
    529         glUseProgram(program);
    530         return program;
    531 }
    532 
    533 inline uint32_t
    534 hs_sp_texture_transform_create()
    535 {
    536         return hs_sp_create_from_src(texture_transform_vert, texture_transform_frag);
    537 }
    538 
    539 inline uint32_t
    540 hs_sp_create_from_src(const char *v_src, const char *f_src)
    541 {
    542         uint32_t v_shader = hs_shader_create(v_src, GL_VERTEX_SHADER);
    543         uint32_t f_shader = hs_shader_create(f_src, GL_FRAGMENT_SHADER);
    544 
    545         return hs_sp_create(v_shader, f_shader);
    546 }
    547 
    548 uint32_t
    549 hs_sp_create_from_file(const char *v_file, const char *f_file)
    550 {
    551         char* v_src = hs_file_read_null_term(v_file);
    552         char* f_src = hs_file_read_null_term(f_file);
    553 
    554         uint32_t sp = hs_sp_create_from_src(v_src, f_src);
    555 
    556         free(v_src);
    557         free(f_src);
    558 
    559         glUseProgram(sp);
    560         return sp;
    561 }
    562 
    563 inline uint32_t
    564 hs_fbo_color_create(const uint32_t width, const uint32_t height, uint32_t* tex)
    565 {
    566         uint32_t fbo;
    567         glGenFramebuffers(1, &fbo);
    568         glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
    569 
    570         glGenTextures(1, tex);
    571         glBindTexture(GL_TEXTURE_2D, *tex);
    572 
    573         // create attached texture
    574         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
    575         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    576         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    577         glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, *tex, 0);
    578 
    579         assert(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
    580         return fbo;
    581 }
    582 
    583 inline void
    584 hs_fbo_draw_to_screen(const uint32_t fbo, const uint32_t src_w, const uint32_t src_h,
    585                       const uint32_t dst_x, const uint32_t dst_y, const uint32_t dst_w, const uint32_t dst_h)
    586 {
    587         glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
    588         glReadBuffer(GL_COLOR_ATTACHMENT0);
    589         glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
    590         hs_clear(0.0f, 0.0f, 0.0f, 1.0f, 0);
    591 
    592         glBlitFramebuffer(0, 0, src_w, src_h, dst_x, dst_y, dst_w, dst_h, GL_COLOR_BUFFER_BIT, GL_NEAREST);
    593         glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
    594 }
    595 
    596 inline hs_shader_program
    597 hs_shader_program_create(const uint32_t sp, hs_vobj vobj)
    598 {
    599         return (hs_shader_program) {
    600                 .p = sp,
    601                 .vobj = vobj,
    602         };
    603 }
    604 
    605 inline void
    606 hs_sp_delete(hs_shader_program sp)
    607 {
    608         glDeleteProgram(sp.p);
    609         hs_vobj_free(sp.vobj);
    610 }
    611 
    612 inline void
    613 hs_avg_frametime_print(const float delta, const float interval)
    614 {
    615         static float prev = 0.0f;
    616         static float acum = 0.0f;
    617         static uint32_t times = 0;
    618         prev += delta;
    619         acum += delta;
    620         times++;
    621 
    622         if (prev > interval) {
    623                 printf("avg frametime: %f\n", (acum/times));
    624                 prev = 0.0f;
    625                 acum = 0.0f;
    626                 times = 0;
    627         }
    628 }
    629 
    630 inline void
    631 hs_avg_fps_print(const float delta, const float interval)
    632 {
    633         static float prev = 0.0f;
    634         static float acum = 0.0f;
    635         static uint32_t times = 0;
    636         prev += delta;
    637         acum += delta;
    638         times++;
    639 
    640         if (prev > interval) {
    641                 printf("avg fps: %f\n", 1000/(acum/times));
    642                 prev = 0.0f;
    643                 acum = 0.0f;
    644                 times = 0;
    645         }
    646 }
    647 
    648 inline void
    649 hs_tex_uniform_set(const uint32_t u_tex, const uint32_t tex)
    650 {
    651         glUniform1i(u_tex, tex);
    652 }
    653 
    654 inline void
    655 hs_tex2d_activate(const uint32_t texture_object, const GLenum texindex)
    656 {
    657         glActiveTexture(texindex);
    658         glBindTexture(GL_TEXTURE_2D, texture_object);
    659 }
    660 
    661 #ifndef NO_STBI
    662 uint32_t
    663 hs_tex2d_create(const char *filename, const GLenum format,
    664                 const GLenum wrap, const GLenum filter)
    665 {
    666         uint32_t tex;
    667         glGenTextures(1, &tex);
    668         glBindTexture(GL_TEXTURE_2D, tex);
    669 
    670         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap);
    671         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap);
    672         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter);
    673         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter);
    674 
    675         int width, height, nr_channels;
    676         unsigned char* texture_data = stbi_load(filename, &width, &height, &nr_channels, 0);
    677         if (!texture_data) {
    678                 fprintf(stderr, "---error loading texture \"%s\"--\n", filename);
    679                 assert(texture_data);
    680         }
    681         glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, texture_data);
    682         glGenerateMipmap(GL_TEXTURE_2D);
    683         stbi_image_free(texture_data);
    684 
    685         return tex;
    686 }
    687 
    688 inline uint32_t
    689 hs_tex2d_create_pixel(const char *filename, const GLenum format)
    690 {
    691         return hs_tex2d_create(filename, format, GL_REPEAT, GL_NEAREST);
    692 }
    693 
    694 inline uint32_t
    695 hs_tex2d_create_size_info_pixel(const char *filename, const GLenum format, int* width, int* height)
    696 {
    697         return hs_tex2d_create_size_info(filename, format, GL_REPEAT, GL_NEAREST, width, height);
    698 }
    699 
    700 uint32_t
    701 hs_tex2d_create_size_info(const char *filename, const GLenum format,
    702                           const GLenum  wrap, const GLenum filter,
    703                           int* width, int* height)
    704 {
    705         uint32_t tex;
    706         glGenTextures(1, &tex);
    707         glBindTexture(GL_TEXTURE_2D, tex);
    708 
    709         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap);
    710         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap);
    711         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter);
    712         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter);
    713 
    714         int nr_channels;
    715         unsigned char* texture_data = stbi_load(filename, width, height, &nr_channels, 0);
    716         if (!texture_data) {
    717                 fprintf(stderr, "---error loading texture \"%s\"--\n", filename);
    718                 assert(texture_data);
    719         }
    720         glTexImage2D(GL_TEXTURE_2D, 0, format, *width, *height, 0, format, GL_UNSIGNED_BYTE, texture_data);
    721         glGenerateMipmap(GL_TEXTURE_2D);
    722         stbi_image_free(texture_data);
    723 
    724         return tex;
    725 }
    726 #endif // NO_STBI
    727 
    728 inline hs_camera
    729 hs_init_fps_camera()
    730 {
    731         return (hs_camera){
    732                 .up = {0.0f, 1.0f, 0.0f},
    733                 .front = {0.0f, 0.0f, -1.0f},
    734                 .yaw = -90.0f
    735         };
    736 }
    737 
    738 inline vec2
    739 hs_aabb2_center(const hs_aabb2 rect)
    740 {
    741         return vec2_add(rect.bl, hs_aabb2_half_size(rect));
    742 }
    743 
    744 inline vec2i
    745 hs_aabb2i_center(const hs_aabb2i rect)
    746 {
    747         return vec2i_add(rect.bl, hs_aabb2i_half_size(rect));
    748 }
    749 
    750 inline vec2
    751 hs_aabb2_size(const hs_aabb2 rect)
    752 {
    753         return vec2_sub(rect.tr, rect.bl);
    754 }
    755 
    756 inline vec2
    757 hs_aabb2_half_size(const hs_aabb2 rect)
    758 {
    759         return vec2_scale(vec2_sub(rect.tr, rect.bl), 0.5f);
    760 }
    761 
    762 inline vec2i
    763 hs_aabb2i_size(const hs_aabb2i rect)
    764 {
    765         return vec2i_sub(rect.tr, rect.bl);
    766 }
    767 
    768 inline vec2i
    769 hs_aabb2i_half_size(const hs_aabb2i rect)
    770 {
    771         return vec2i_div(vec2i_sub(rect.tr, rect.bl), 2);
    772 }
    773 
    774 inline hs_aabb2
    775 hs_aabb2_from_rect2(const hs_rect2 r)
    776 {
    777         return(hs_aabb2){
    778                 .bl = vec2_sub(r.pos, r.half_size),
    779                 .tr = vec2_add(r.pos, r.half_size),
    780         };
    781 }
    782 
    783 inline uint32_t
    784 hs_bsp_recti_split_in_place_append(hs_aabb2i* rects, const uint32_t new_rect_index, const vec2i min_rect_size)
    785 {
    786         // this way of checking if the room is too small does not respect very non-square rectangles
    787         // TOOD: maybe change this^
    788 
    789         uint32_t rect_index;
    790         for (uint32_t tries = 0; tries < 7; tries++) {
    791                 rect_index = rand() % new_rect_index;
    792 
    793                 // half rect size instead of size since we are trying to split it
    794                 vec2i half_rect_size = hs_aabb2i_half_size(rects[rect_index]);
    795                 if (min_rect_size.x <= half_rect_size.x && min_rect_size.y <= half_rect_size.y)
    796                         goto random_rect_found;
    797         }
    798 
    799         // randomly trying rooms failed,
    800         // looping through all rooms to try and find a suitable room
    801         for (rect_index = 0; rect_index < new_rect_index; rect_index++) {
    802 
    803                 vec2i half_rect_size = hs_aabb2i_half_size(rects[rect_index]);
    804                 if (min_rect_size.x <= half_rect_size.x && min_rect_size.y <= half_rect_size.y)
    805                         goto random_rect_found;
    806         }
    807 
    808         return false;
    809 
    810         random_rect_found:
    811 
    812         rects[new_rect_index] = rects[rect_index];
    813         const vec2i rect_size = hs_aabb2i_size(rects[rect_index]);
    814 
    815         // TODO maybe add some randomness to this
    816         const uint32_t axis = rect_size.x >= rect_size.y ? 0 : 1;
    817 
    818         const int32_t axis_half_size = rect_size.xy[axis] / 2;
    819         const int32_t axis_center = rects[rect_index].bl.xy[axis] + axis_half_size;
    820         const int32_t max_split = axis_half_size - min_rect_size.xy[axis];
    821 
    822         int32_t split = 0;
    823         if (max_split)
    824                 split = (rand() % (max_split * 2)) - max_split;
    825 
    826         rects[rect_index].bl.xy[axis] = axis_center - split;
    827         rects[new_rect_index].tr.xy[axis] = axis_center - split - 1;
    828         return true;
    829 }
    830 
    831 inline uint32_t
    832 hs_rect2_is_inside(const hs_rect2 r1, const hs_rect2 r2, float* lenabs)
    833 {
    834         const vec2 distance = vec2_add(r1.half_size, r2.half_size);
    835         const vec2 diff = vec2_sub(r1.pos, r2.pos);
    836         const vec2 diffabs = {fabs(diff.x), fabs(diff.y)};
    837         *lenabs = vec2_len(diffabs);
    838 
    839         if (diffabs.x >= distance.x || diffabs.y >= distance.y)
    840                 return false;
    841         return true;
    842 }
    843 
    844 void
    845 hs_entity2_force_inside_rects(hs_rect2* e, hs_rect2* rects, const uint32_t rectc)
    846 {
    847         float len_to_closest_rect = INFINITY;
    848         uint32_t closest_rect = 0;
    849         for (uint32_t i = 0; i < rectc; i++) {
    850                 float len;
    851                 hs_rect2 r = rects[i];
    852                 r.half_size = vec2_sub(r.half_size, vec2_scale(e->half_size, 2.0f));
    853                 if (hs_rect2_is_inside(*e, r, &len)) return;
    854 
    855                 if (len < len_to_closest_rect) {
    856                         closest_rect = i;
    857                         len_to_closest_rect = len;
    858                 }
    859         }
    860 
    861         /* move player inside closest room */
    862 
    863         const vec2 distance = vec2_sub(rects[closest_rect].half_size, e->half_size);
    864         vec2 diff = vec2_sub(e->pos, rects[closest_rect].pos);
    865         const vec2 diffabs = {fabs(diff.x), fabs(diff.y)};
    866         const float extra = 1.0f - 0.001f;
    867 
    868         if (diffabs.x > diffabs.y) {
    869                 const float mul = (diff.x / diffabs.x) * extra;
    870                 CLAMP(diff.x, distance.x, -distance.x);
    871                 e->pos = vec2_add(rects[closest_rect].pos, (vec2){distance.x * mul, diff.y});
    872         } else {
    873                 const float mul = (diff.y / diffabs.y) * extra;
    874                 CLAMP(diff.y, distance.y, -distance.y);
    875                 e->pos = vec2_add(rects[closest_rect].pos, (vec2){diff.x, distance.y * mul});
    876         }
    877 }
    878 
    879 inline void
    880 hs_entity2_collide(hs_rect2* r1, const hs_rect2* r2)
    881 {
    882         const vec2 distance = vec2_add(r1->half_size, r2->half_size);
    883         vec2 diff = vec2_sub(r1->pos, r2->pos);
    884 
    885         if (fabs(diff.x) >= distance.x || fabs(diff.y) >= distance.y) return;
    886 
    887         if (diff.x == 0) diff.x = random_float_negative() * 0.00000001;
    888         if (diff.y == 0) diff.y = random_float_negative() * 0.00000001;
    889         const vec2 diffabs = {fabs(diff.x), fabs(diff.y)};
    890 
    891         const float extra = 1.0001;
    892 
    893         if (diffabs.x > diffabs.y) {
    894                 const float mul = (diff.x / diffabs.x) * extra;
    895                 r1->pos = vec2_add(r2->pos, (vec2){distance.x * mul, diff.y});
    896         } else {
    897                 const float mul = (diff.y / diffabs.y) * extra;
    898                 r1->pos = vec2_add(r2->pos, (vec2){diff.x, distance.y * mul});
    899         }
    900         //static uint32_t col = 0;
    901         //printf("collisions %d\n", col++);
    902 }
    903 
    904 inline void
    905 hs_camera_move_front(hs_camera* camera, const float scale)
    906 {
    907         camera->pos = vec3_add(camera->pos, vec3_scale(camera->front, scale));
    908 }
    909 
    910 inline void
    911 hs_camera_move_right(hs_camera* camera, const float scale)
    912 {
    913         camera->pos = vec3_add(camera->pos, vec3_scale(vec3_norm(vec3_cross(camera->front, camera->up)), scale));
    914 }
    915 
    916 inline void
    917 hs_camera_move_frontv(const hs_camera camera, vec3* new_pos)
    918 {
    919         *new_pos = vec3_add(*new_pos, camera.front);
    920 }
    921 
    922 inline void
    923 hs_camera_move_rightv(const hs_camera camera, vec3* new_pos)
    924 {
    925         *new_pos = vec3_add(*new_pos, vec3_norm(vec3_cross(camera.front, camera.up)));
    926 }
    927 
    928 inline void
    929 hs_camera_move_backv(const hs_camera camera, vec3* new_pos)
    930 {
    931         *new_pos = vec3_sub(*new_pos, camera.front);
    932 }
    933 
    934 inline void
    935 hs_camera_move_leftv(const hs_camera camera, vec3* new_pos)
    936 {
    937         *new_pos = vec3_sub(*new_pos, vec3_norm(vec3_cross(camera.front, camera.up)));
    938 }
    939 
    940 inline void
    941 hs_camera_look_at(mat4 view, const hs_camera camera)
    942 {
    943         mat4_look_at(view, camera.pos,
    944                      vec3_add(camera.pos, camera.front),
    945                      camera.up);
    946 }
    947 
    948 inline vec2
    949 hs_pos_to_offset(const float xpos, const float ypos, const float sens)
    950 {
    951         static bool first_mouse = true;
    952         static float last_x = 400;
    953         static float last_y = 300;
    954 
    955         if (first_mouse) {
    956                 last_x = xpos;
    957                 last_y = ypos;
    958                 first_mouse = false;
    959         }
    960         float xoffset = xpos - last_x;
    961         float yoffset = last_y - ypos;
    962         last_x = xpos;
    963         last_y = ypos;
    964 
    965         return (vec2){
    966                 xoffset * sens,
    967                 yoffset * sens,
    968         };
    969 }
    970 
    971 inline void
    972 hs_camera_update_front(hs_camera* camera)
    973 {
    974         camera->front = vec3_norm((vec3){
    975                 cosf(deg2rad(camera->yaw)) * cosf(deg2rad(camera->pitch)),
    976                 sinf(deg2rad(camera->pitch)),
    977                 sinf(deg2rad(camera->yaw)) * cosf(deg2rad(camera->pitch)),
    978         });
    979 }
    980 
    981 inline void
    982 hs_camera2_smooth_move_to_goal(hs_camera2_smooth* cam, const float scale)
    983 {
    984         cam->curr = vec2_add(cam->curr, vec2_scale(vec2_sub(cam->goal, cam->curr), scale));
    985 }
    986 
    987 vec2
    988 hs_px_coord_to_global(const vec2 cam_offset, const vec2 scale, const hs_aabb2 res, const vec2 px)
    989 {
    990         if (px.x < res.bl.x || px.y < res.bl.y) return (vec2){NAN, NAN};
    991         if (px.x > res.tr.x || px.y > res.tr.y) return (vec2){NAN, NAN};
    992 
    993         vec2 global;
    994         global.x = MAP(px.x, res.bl.x, res.tr.x, -1/scale.x, 1/scale.x);
    995         global.y = MAP(px.y, res.bl.y, res.tr.y, -1/scale.y, 1/scale.y);
    996 
    997         return vec2_add(global, cam_offset);
    998 }
    999 
   1000 inline void
   1001 hs_camera2_smooth_view(mat4 view, const hs_camera2_smooth cam)
   1002 {
   1003         mat4_translate(view, (vec3){-cam.curr.x, -cam.curr.y, 0.0f});
   1004 }
   1005 
   1006 inline void
   1007 hs_tex_square_set_pos(hs_tex_square* square, const hs_aabb2 pos)
   1008 {
   1009         // triangle one
   1010         square->c[0].pos.x = pos.bl.x;
   1011         square->c[0].pos.y = pos.bl.y;
   1012 
   1013         square->c[1].pos.x = pos.tr.x;
   1014         square->c[1].pos.y = pos.bl.y;
   1015 
   1016         square->c[2].pos.x = pos.tr.x;
   1017         square->c[2].pos.y = pos.tr.y;
   1018 
   1019         // triangle two
   1020         square->c[3].pos.x = pos.tr.x;
   1021         square->c[3].pos.y = pos.tr.y;
   1022 
   1023         square->c[4].pos.x = pos.bl.x;
   1024         square->c[4].pos.y = pos.tr.y;
   1025 
   1026         square->c[5].pos.x = pos.bl.x;
   1027         square->c[5].pos.y = pos.bl.y;
   1028 }
   1029 
   1030 void
   1031 hs_tex_square_set_tex(hs_tex_square* square, hs_aabb2 tex)
   1032 {
   1033         // crop it slightly, this makes the tiles next to the selected tile not accidentaly appear
   1034         tex.bl.x += 0.001f;
   1035         tex.bl.y += 0.001f;
   1036         tex.bl.x -= 0.001f;
   1037         tex.tr.y -= 0.001f;
   1038 
   1039         // bottom left
   1040         square->c[0].tex.x = tex.bl.x;
   1041         square->c[0].tex.y = tex.bl.y;
   1042 
   1043         // bottom right
   1044         square->c[1].tex.x = tex.tr.x;
   1045         square->c[1].tex.y = tex.bl.y;
   1046 
   1047         // top right
   1048         square->c[2].tex.x = tex.tr.x;
   1049         square->c[2].tex.y = tex.tr.y;
   1050 
   1051         // top right
   1052         square->c[3].tex.x = tex.tr.x;
   1053         square->c[3].tex.y = tex.tr.y;
   1054 
   1055         // top left
   1056         square->c[4].tex.x = tex.bl.x;
   1057         square->c[4].tex.y = tex.tr.y;
   1058 
   1059         // bottom left
   1060         square->c[5].tex.x = tex.bl.x;
   1061         square->c[5].tex.y = tex.bl.y;
   1062 }
   1063 
   1064 inline void
   1065 hs_tilemap_set(hs_tilemap* tilemap, const uint32_t vertex, uint32_t tile)
   1066 {
   1067         // make tiles start at 0 instead of 1
   1068         tile++;
   1069 
   1070         const float width      = 1.0f/tilemap->tileset_width;
   1071         const float height     = 1.0f/tilemap->tileset_height;
   1072         const float ypos       = height * ceilf((float)tile / (float)tilemap->tileset_width);
   1073         float xpos             = width * (tile % tilemap->tileset_width);
   1074         if (xpos == 0.0f) xpos = 1.0f;
   1075 
   1076         hs_aabb2 tex = {
   1077                 .bl = (vec2){xpos - width, ypos - height},
   1078                 .tr = (vec2){xpos, ypos},
   1079         };
   1080 
   1081         hs_tex_square_set_tex(&tilemap->vertices[vertex], tex);
   1082 }
   1083 
   1084 void
   1085 hs_tilemap_setall(hs_tilemap* tilemap, const uint32_t tile)
   1086 {
   1087         const uint32_t tilemap_size = tilemap->width * tilemap->height;
   1088         for(uint32_t v = 0; v < tilemap_size; v++)
   1089                 hs_tilemap_set(tilemap, v, tile);
   1090 }
   1091 
   1092 inline void
   1093 hs_tilemap_set_xy(hs_tilemap* tilemap, const uint32_t x, uint32_t y, uint32_t tile)
   1094 {
   1095         hs_tilemap_set(tilemap, y * tilemap->width + x, tile);
   1096 }
   1097 
   1098 inline uint32_t
   1099 hs_tilemap_sizeof(const hs_tilemap tilemap)
   1100 {
   1101         return sizeof(hs_tex_square) * tilemap.width * tilemap.height;
   1102 }
   1103 
   1104 void
   1105 hs_tilemap_init(hs_tilemap* tilemap, const uint32_t default_tex)
   1106 {
   1107         assert(tilemap->width);
   1108         assert(tilemap->height);
   1109         if (tilemap->tile_width <= 0.0f)   tilemap->tile_width = 1.0f/tilemap->width;
   1110         if (tilemap->tile_height <= 0.0f)  tilemap->tile_height = 1.0f/tilemap->height;
   1111         if (tilemap->tileset_width == 0)   tilemap->tileset_width = 1;
   1112         if (tilemap->tileset_height == 0)  tilemap->tileset_height = 1;
   1113         if (tilemap->tex == 0) tilemap->tex = hs_default_missing_tex;
   1114 
   1115         if (tilemap->vertices) {
   1116                 free(tilemap->vertices);
   1117                 tilemap->vertices = NULL;
   1118         }
   1119 
   1120         tilemap->vertices = malloc(hs_tilemap_sizeof(*tilemap));
   1121         assert(tilemap->vertices);
   1122 
   1123         const float offset_x_default = -(float)tilemap->width * tilemap->tile_width + tilemap->tile_width;
   1124         vec2 offset = {offset_x_default, -(float)tilemap->height * tilemap->tile_height + tilemap->tile_height};
   1125         uint32_t vertex = 0;
   1126         hs_aabb2 square;
   1127 
   1128         for (uint32_t y = 0; y < tilemap->height; y++) {
   1129                 square.tr.y = offset.y + tilemap->tile_height;
   1130                 square.bl.y = offset.y - tilemap->tile_height;
   1131 
   1132                 for (uint32_t x = 0; x < tilemap->width; x++) {
   1133                         square.tr.x = offset.x + tilemap->tile_width;
   1134                         square.bl.x = offset.x - tilemap->tile_width;
   1135 
   1136                         hs_tex_square_set_pos(&tilemap->vertices[vertex], square);
   1137                         hs_tilemap_set(tilemap, vertex, default_tex);
   1138 
   1139                         offset.x += tilemap->tile_width * 2.0f;
   1140                         vertex++;
   1141                 }
   1142 
   1143                 offset.x = offset_x_default;
   1144                 offset.y += tilemap->tile_height * 2.0f;
   1145         }
   1146 
   1147         if (!tilemap->sp.p) {
   1148                 hs_vobj vobj = hs_vobj_create(castf(tilemap->vertices), hs_tilemap_sizeof(*tilemap), 0, 0, GL_DYNAMIC_DRAW, 1);
   1149                 tilemap->sp = hs_shader_program_create(hs_sp_texture_transform_create(), vobj);
   1150 
   1151                 hs_tex_uniform_set(hs_uniform_create(tilemap->sp.p, "u_tex"), 0);
   1152                 tilemap->sp.coord = hs_uniform_coord_create(tilemap->sp.p, "u_model", "u_view", "u_proj");
   1153                 hs_uniform_mat4_set(tilemap->sp.coord.proj, (mat4)MAT4_IDENTITY);
   1154 
   1155                 hs_vattrib_enable_float(0, 2, 4, 0);
   1156                 hs_vattrib_enable_float(1, 2, 4, 2);
   1157 
   1158         } else {
   1159                 hs_tilemap_update_vbo(*tilemap);
   1160         }
   1161 }
   1162 
   1163 inline void
   1164 hs_tilemap_update_vbo(const hs_tilemap tilemap)
   1165 {
   1166         glBindBuffer(GL_ARRAY_BUFFER, tilemap.sp.vobj.vbo);
   1167         glBufferData(GL_ARRAY_BUFFER, hs_tilemap_sizeof(tilemap), castf(tilemap.vertices), GL_DYNAMIC_DRAW);
   1168 }
   1169 
   1170 inline void
   1171 hs_tilemap_draw(const hs_tilemap tilemap)
   1172 {
   1173         hs_sp_use(tilemap.sp);
   1174         glDrawArrays(GL_TRIANGLES, 0, 6 * tilemap.width * tilemap.height);
   1175 }
   1176 
   1177 inline void
   1178 hs_tilemap_free(hs_tilemap* tilemap)
   1179 {
   1180         free(tilemap->vertices);
   1181         hs_sp_delete(tilemap->sp);
   1182         glDeleteTextures(1, &tilemap->tex);
   1183 }
   1184 
   1185 inline void
   1186 hs_aroom_set_xy(hs_aroom* aroom, const uint16_t x, const uint16_t y, const uint16_t data)
   1187 {
   1188         aroom->data[x + y * aroom->width] = data;
   1189 }
   1190 
   1191 void
   1192 hs_aroom_to_tilemap(const hs_aroom aroom, hs_tilemap* tilemap, const uint16_t layer)
   1193 {
   1194         tilemap->width = aroom.width;
   1195         tilemap->height = aroom.height;
   1196         hs_tilemap_init(tilemap, 0);
   1197 
   1198         hs_aroom_set_tilemap(aroom, tilemap, layer);
   1199 }
   1200 
   1201 inline uint8_t
   1202 hs_aroom_get_xy(const hs_aroom aroom, const uint16_t x, const uint16_t y)
   1203 {
   1204         return aroom.data[x + y * aroom.width];
   1205 }
   1206 
   1207 void
   1208 hs_aroom_set_tilemap(const hs_aroom aroom, hs_tilemap* tilemap, const uint16_t layer)
   1209 {
   1210         assert(tilemap->width  == aroom.width);
   1211         assert(tilemap->height == aroom.height);
   1212 
   1213         if (layer > 1) {
   1214                 const uint32_t offset = (aroom.width * aroom.height * layer) - (aroom.width * aroom.height);
   1215                 for(uint32_t i = 0; i < aroom.width * aroom.height; i++)
   1216                         hs_tilemap_set(tilemap, i, aroom.data[i + offset]);
   1217         } else {
   1218                 for(uint32_t i = 0; i < aroom.width * aroom.height; i++)
   1219                         hs_tilemap_set(tilemap, i, aroom.data[i]);
   1220         }
   1221         hs_tilemap_update_vbo(*tilemap);
   1222 }
   1223 
   1224 void
   1225 hs_aroom_set_tilemap_offsetv(const hs_aroom aroom, hs_tilemap* tilemap, const uint16_t layer, const vec2i offset)
   1226 {
   1227         assert(tilemap->width  >= offset.x + aroom.width);
   1228         assert(tilemap->height >= offset.y + aroom.height);
   1229 
   1230         if (layer > 1) {
   1231                 const uint32_t layer_offset = (aroom.width * aroom.height * layer) - (aroom.width * aroom.height);
   1232                 uint32_t i = 0;
   1233                 for(uint32_t x = offset.x; x < aroom.width; x++)
   1234                         for(uint32_t y = offset.y; y < aroom.height; y++)
   1235                                 hs_tilemap_set_xy(tilemap, x, y, aroom.data[layer_offset + i++]);
   1236         } else {
   1237                 uint32_t i = 0;
   1238                 for(uint32_t x = offset.x; x < offset.x + aroom.width; x++)
   1239                         for(uint32_t y = offset.y; y < offset.y + aroom.height; y++)
   1240                                 hs_tilemap_set_xy(tilemap, x, y, aroom.data[i++]);
   1241         }
   1242 }
   1243 
   1244 vec2
   1245 hs_tilemap_pos_to_global(const hs_tilemap tilemap, vec2i pos)
   1246 {
   1247         return (vec2) {
   1248                 .x = (float)pos.x * 2.0f * tilemap.tile_width  - (float)tilemap.width  * tilemap.tile_width,
   1249                 .y = (float)pos.y * 2.0f * tilemap.tile_height - (float)tilemap.height * tilemap.tile_height,
   1250         };
   1251 }
   1252 
   1253 uint32_t
   1254 hs_dyn_tilemap_sizeof(const hs_dyn_tilemap tilemap)
   1255 {
   1256         return sizeof(hs_tex_square) * tilemap.vertices.len;
   1257 }
   1258 
   1259 void
   1260 hs_dyn_tilemap_init(hs_dyn_tilemap* tilemap, size_t size)
   1261 {
   1262         if (tilemap->tile_height    <= 0.0f) tilemap->tile_height = 0.1f;
   1263         if (tilemap->tile_width     <= 0.0f) tilemap->tile_width = 0.1f;
   1264         if (tilemap->tileset_height == 0)    tilemap->tileset_height = 1;
   1265         if (tilemap->tileset_width  == 0)    tilemap->tileset_width = 1;
   1266         if (tilemap->tex            == 0)    tilemap->tex = hs_default_missing_tex;
   1267         if (size                    == 0)    size = 10000;
   1268 
   1269         tilemap->vertices = hs_dynarr_init(hs_tex_square, size);
   1270 
   1271         hs_vobj vobj = hs_vobj_create(castf(tilemap->vertices.data), hs_dyn_tilemap_sizeof(*tilemap), 0, 0, GL_DYNAMIC_DRAW, 1);
   1272         tilemap->sp = hs_shader_program_create(hs_sp_texture_transform_create(), vobj);
   1273 
   1274         hs_tex_uniform_set(hs_uniform_create(tilemap->sp.p, "u_tex"), 0);
   1275         tilemap->sp.coord = hs_uniform_coord_create(tilemap->sp.p, "u_model", "u_view", "u_proj");
   1276         hs_uniform_mat4_set(tilemap->sp.coord.proj, (mat4)MAT4_IDENTITY);
   1277 
   1278         hs_vattrib_enable_float(0, 2, 4, 0);
   1279         hs_vattrib_enable_float(1, 2, 4, 2);
   1280 }
   1281 
   1282 inline void
   1283 hs_dyn_tilemap_clear(hs_dyn_tilemap* tilemap)
   1284 {
   1285         hs_dynarr_clear(tilemap->vertices);
   1286 }
   1287 
   1288 inline void
   1289 hs_dyn_tilemap_free(hs_dyn_tilemap* tilemap)
   1290 {
   1291         hs_dynarr_free(tilemap->vertices);
   1292         hs_sp_delete(tilemap->sp);
   1293         glDeleteTextures(1, &tilemap->tex);
   1294 }
   1295 
   1296 void
   1297 hs_dyn_tilemap_push(hs_dyn_tilemap* tilemap, const vec2i pos, uint32_t tile)
   1298 {
   1299         hs_tex_square new_tile;
   1300 
   1301         // make tiles start at 0 instead of 1
   1302         tile++;
   1303 
   1304         const float width      = 1.0f/tilemap->tileset_width;
   1305         const float height     = 1.0f/tilemap->tileset_height;
   1306         const float ypos       = height * ceilf((float)tile / (float)tilemap->tileset_width);
   1307         float xpos             = width * (tile % tilemap->tileset_width);
   1308         if (xpos == 0.0f) xpos = 1.0f;
   1309 
   1310         hs_aabb2 tex = {
   1311                 .bl = (vec2){xpos - width, ypos - height},
   1312                 .tr = (vec2){xpos, ypos},
   1313         };
   1314         hs_tex_square_set_tex(&new_tile, tex);
   1315 
   1316         vec2 offset = hs_dyn_tilemap_pos_to_global(*tilemap, pos);
   1317         hs_aabb2 square_pos;
   1318         square_pos.bl.x = offset.x - tilemap->tile_width;
   1319         square_pos.bl.y = offset.y - tilemap->tile_height;
   1320         square_pos.tr.x = offset.x + tilemap->tile_width;
   1321         square_pos.tr.y = offset.y + tilemap->tile_height;
   1322         hs_tex_square_set_pos(&new_tile, square_pos);
   1323 
   1324         hs_dynarr_push(tilemap->vertices, hs_tex_square, new_tile);
   1325 }
   1326 
   1327 void
   1328 hs_dyn_tilemap_update_vbo(const hs_dyn_tilemap tilemap)
   1329 {
   1330         glBindBuffer(GL_ARRAY_BUFFER, tilemap.sp.vobj.vbo);
   1331         glBufferData(GL_ARRAY_BUFFER, hs_dyn_tilemap_sizeof(tilemap), castf(tilemap.vertices.data), GL_DYNAMIC_DRAW);
   1332 }
   1333 
   1334 inline void
   1335 hs_dyn_tilemap_draw(const hs_dyn_tilemap tilemap)
   1336 {
   1337         hs_sp_use(tilemap.sp);
   1338         glDrawArrays(GL_TRIANGLES, 0, 6 * tilemap.vertices.len);
   1339 }
   1340 
   1341 void
   1342 hs_aroom_push_dyn_tilemap(const hs_aroom aroom, hs_dyn_tilemap* tilemap, const uint16_t layer, const vec2i offset)
   1343 {
   1344         vec2i pos;
   1345         if (layer > 1) {
   1346                 const uint32_t layer_offset = (aroom.width * aroom.height * layer) - (aroom.width * aroom.height);
   1347                 uint32_t i = 0;
   1348                 for(uint32_t x = offset.x; x < aroom.width; x++)
   1349                         for(uint32_t y = offset.y; y < aroom.height; y++)
   1350                                 hs_dyn_tilemap_push(tilemap, (vec2i){x + offset.x, y + offset.y},
   1351                                                     aroom.data[layer_offset + i++]);
   1352         } else {
   1353                 uint32_t i = 0;
   1354                 for(uint32_t x = 0; x < aroom.width; x++)
   1355                         for(uint32_t y = 0; y < aroom.height; y++)
   1356                                 hs_dyn_tilemap_push(tilemap, (vec2i){x + offset.x, y + offset.y},
   1357                                                     aroom.data[i++]);
   1358         }
   1359 }
   1360 
   1361 vec2
   1362 hs_dyn_tilemap_pos_to_global(const hs_dyn_tilemap tilemap, vec2i pos)
   1363 {
   1364         return (vec2) {
   1365                 .x = (float)pos.x * 2.0f * tilemap.tile_width,
   1366                 .y = (float)pos.y * 2.0f * tilemap.tile_height,
   1367         };
   1368 }
   1369 
   1370 inline hs_shader_program
   1371 hs_sp_sprite_create(const float width, const float height, const float screen_size)
   1372 {
   1373         const float vertices[] = HS_DEFAULT_SQUARE_SCALED_TEX_VERT_ONLY(
   1374                 width/screen_size, width/screen_size);
   1375 
   1376         hs_vobj vobj = hs_vobj_create(vertices, sizeof(vertices), 0, 0, GL_STATIC_DRAW, 1);
   1377         hs_shader_program sp = hs_shader_program_create(hs_sp_texture_transform_create(), vobj);
   1378         sp.coord = hs_uniform_coord_create(sp.p, "u_model", "u_view", "u_proj");
   1379         hs_uniform_mat4_set(sp.coord.proj, (mat4)MAT4_IDENTITY);
   1380 
   1381         hs_vattrib_enable_float(0, 2, 4, 0);
   1382         hs_vattrib_enable_float(1, 2, 4, 2);
   1383 
   1384         return sp;
   1385 }
   1386 
   1387 inline void
   1388 hs_sprite_draw_current()
   1389 {
   1390         glDrawArrays(GL_TRIANGLES, 0, 6);
   1391 }
   1392 
   1393 inline hs_entity2
   1394 hs_entity2_create(hs_entity2_hot* hot, hs_shader_program sp, hs_tex tex)
   1395 {
   1396         hot->sp = sp;
   1397         hot->tex = tex;
   1398         return (hs_entity2){.hot = hot};
   1399 }
   1400 
   1401 inline uint32_t
   1402 hs_vao_create(const uint32_t count)
   1403 {
   1404         uint32_t vao;
   1405         glGenVertexArrays(count, &vao);
   1406         glBindVertexArray(vao);
   1407         return vao;
   1408 }
   1409 
   1410 inline uint32_t
   1411 hs_vbo_create(const float *vbuff, const uint32_t buffsize, const GLenum usage, const uint32_t count)
   1412 {
   1413         uint32_t vbo;
   1414         glGenBuffers(count, &vbo);
   1415         glBindBuffer(GL_ARRAY_BUFFER, vbo);
   1416         glBufferData(GL_ARRAY_BUFFER, buffsize, vbuff, usage);
   1417         return vbo;
   1418 }
   1419 
   1420 inline uint32_t
   1421 hs_ebo_create(const uint32_t *ibuff, const uint32_t buffsize, const GLenum usage, const uint32_t count)
   1422 {
   1423         if(!buffsize) return 0;
   1424         uint32_t ebo;
   1425         glGenBuffers(1, &ebo);
   1426         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
   1427         glBufferData(GL_ELEMENT_ARRAY_BUFFER, buffsize, ibuff, usage);
   1428         return ebo;
   1429 }
   1430 
   1431 inline hs_vobj
   1432 hs_vobj_create(const float    *vbuff, const uint32_t vbuffsize,
   1433                const uint32_t *ibuff, const uint32_t ibuffsize,
   1434                const GLenum    usage, const uint32_t count)
   1435 {
   1436         hs_vobj vobj;
   1437         vobj.vbo = hs_vbo_create(vbuff, vbuffsize, usage, count);
   1438         vobj.ebo = hs_ebo_create(ibuff, ibuffsize, usage, count);
   1439         vobj.vao = hs_vao_create(count);
   1440         vobj.count = count;
   1441         return vobj;
   1442 }
   1443 
   1444 inline void
   1445 hs_vobj_free(hs_vobj vobj)
   1446 {
   1447         glDeleteVertexArrays(vobj.count, &vobj.vao);
   1448         if (vobj.ebo)
   1449                 glDeleteBuffers(vobj.count, &vobj.ebo);
   1450         glDeleteBuffers(vobj.count, &vobj.vbo);
   1451 }
   1452 
   1453 inline void
   1454 hs_fps_callback_init(const hs_game_data gd, void(*mouse_callback)(GLFWwindow*, double xpos, double ypos))
   1455 {
   1456         glfwSetInputMode(gd.window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
   1457         glfwSetCursorPosCallback(gd.window, mouse_callback);
   1458 }
   1459 
   1460 inline static void
   1461 hs_disable_vsync()
   1462 {
   1463         glfwSwapInterval(0);
   1464 }
   1465 
   1466 enum hs_init_flags {
   1467         HS_NO_VSYNC = 1 << 0,
   1468         HS_WIREFRAME_MODE = 1 << 1,
   1469         HS_BLEND_MODE = 1 << 2,
   1470         HS_DEPTH_TESTING = 1 << 3,
   1471 };
   1472 
   1473 inline static void
   1474 hs_init(hs_game_data* gd, const char *name, void(*framebuffer_size_callback)(GLFWwindow*, int, int), const uint32_t flags)
   1475 {
   1476         glfwInit();
   1477         glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
   1478         glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
   1479         glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
   1480 
   1481         if (!gd->width) gd->width = 800;
   1482         if (!gd->height) gd->height = 600;
   1483 
   1484         GLFWwindow* window = glfwCreateWindow(gd->width, gd->height, name, NULL, NULL);
   1485         assert(window);
   1486 
   1487         glfwMakeContextCurrent(window);
   1488         assert(gladLoadGLLoader((GLADloadproc)glfwGetProcAddress));
   1489         glViewport(0, 0, gd->width, gd->height);
   1490 
   1491 #ifndef NO_STBI
   1492         {
   1493                 glGenTextures(1, &hs_default_missing_tex);
   1494                 glBindTexture(GL_TEXTURE_2D, hs_default_missing_tex);
   1495 
   1496                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
   1497                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
   1498                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
   1499                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
   1500 
   1501                 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 32, 32, 0, GL_RGBA, GL_UNSIGNED_BYTE, hs_default_missing_tex_data);
   1502                 glGenerateMipmap(GL_TEXTURE_2D);
   1503         }
   1504 #endif // NO_STBI
   1505 
   1506         if (flags & HS_WIREFRAME_MODE) glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
   1507         if (flags & HS_NO_VSYNC) hs_disable_vsync();
   1508         if (flags & HS_DEPTH_TESTING) glEnable(GL_DEPTH_TEST);
   1509 
   1510         if (flags & HS_BLEND_MODE) {
   1511                 glEnable(GL_BLEND);
   1512                 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
   1513         }
   1514         if (framebuffer_size_callback) {
   1515                 glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
   1516                 int width, height;
   1517                 glfwGetFramebufferSize(window, &width, &height);
   1518                 framebuffer_size_callback(window, width, height);
   1519         }
   1520 
   1521         gd->window = window;
   1522 }
   1523 
   1524 inline static void
   1525 hs_poll_input()
   1526 {
   1527         glfwPollEvents();
   1528 }
   1529 
   1530 inline static void
   1531 hs_end_frame(const hs_game_data gd)
   1532 {
   1533         glfwSwapBuffers(gd.window);
   1534 }
   1535 
   1536 inline static void
   1537 hs_exit() {glfwTerminate();}
   1538 
   1539 #endif // HS_IMPL
   1540 
   1541 #endif // HS_GRAPHICS_H_