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_