opengl moment #1
| @@ -58,7 +58,7 @@ source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${SOURCE_FILES}) | |||||||
| set_target_properties(${PROJECT_NAME} PROPERTIES | set_target_properties(${PROJECT_NAME} PROPERTIES | ||||||
|                                       C_STANDARD 11 |                                       C_STANDARD 11 | ||||||
|                                       C_STANDARD_REQUIRED ON |                                       C_STANDARD_REQUIRED ON | ||||||
|                                       C_EXTENSIONS OFF) |                                       C_EXTENSIONS ON) # extensions are required by stb_ds.h | ||||||
|  |  | ||||||
| # distribution definitions | # distribution definitions | ||||||
| set(ORGANIZATION_NAME "wanp" CACHE STRING | set(ORGANIZATION_NAME "wanp" CACHE STRING | ||||||
|   | |||||||
| @@ -23,9 +23,7 @@ typedef struct context { | |||||||
|     struct sprite_primitive *render_queue_sprites; |     struct sprite_primitive *render_queue_sprites; | ||||||
|     struct rect_primitive *render_queue_rectangles; |     struct rect_primitive *render_queue_rectangles; | ||||||
|     struct circle_primitive *render_queue_circles; |     struct circle_primitive *render_queue_circles; | ||||||
|  |     struct mesh_batch_item *uncolored_mesh_batches; | ||||||
|     struct mesh_batch *uncolored_mesh_batches; /* texture_cache reflected */ |  | ||||||
|     struct mesh_batch_item *uncolored_mesh_batches_loners; /* path reflected */ |  | ||||||
|  |  | ||||||
|     struct audio_channel_item *audio_channels; |     struct audio_channel_item *audio_channels; | ||||||
|     SDL_AudioDeviceID audio_device; |     SDL_AudioDeviceID audio_device; | ||||||
|   | |||||||
| @@ -2,6 +2,7 @@ | |||||||
| #define PRIVATE_RENDERING_H | #define PRIVATE_RENDERING_H | ||||||
|  |  | ||||||
| #include "../rendering.h" | #include "../rendering.h" | ||||||
|  | #include "../textures.h" | ||||||
| #include "../util.h" | #include "../util.h" | ||||||
|  |  | ||||||
| #include <SDL2/SDL.h> | #include <SDL2/SDL.h> | ||||||
| @@ -13,9 +14,8 @@ struct sprite_primitive { | |||||||
|     t_frect rect; |     t_frect rect; | ||||||
|     t_color color; |     t_color color; | ||||||
|     double rotation; |     double rotation; | ||||||
|     char *path; |  | ||||||
|     SDL_BlendMode blend_mode; |     SDL_BlendMode blend_mode; | ||||||
|     int atlas_index; |     t_texture_key texture_key; | ||||||
|     int layer; |     int layer; | ||||||
|     bool flip_x; |     bool flip_x; | ||||||
|     bool flip_y; |     bool flip_y; | ||||||
| @@ -32,26 +32,39 @@ struct circle_primitive { | |||||||
|     t_fvec2 position; |     t_fvec2 position; | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | /* union for in-place recalculation of texture coordinates */ | ||||||
|  | union uncolored_space_triangle { | ||||||
|  |     /* pending for sending, uvs are not final as texture atlases could update */ | ||||||
|  |     struct uncolored_space_triangle_primitive { | ||||||
|  |         t_fvec3 v0; | ||||||
|  |         t_fvec2 uv0; /* in pixels */ | ||||||
|  |         t_fvec3 v1; | ||||||
|  |         t_fvec2 uv1; /* in pixels */ | ||||||
|  |         t_fvec3 v2; | ||||||
|  |         t_fvec2 uv2; /* in pixels */ | ||||||
|  |     } primitive; | ||||||
|  |  | ||||||
|  |     /* structure that is passed in opengl vertex array */ | ||||||
|  |     struct uncolored_space_triangle_payload { | ||||||
|  |         t_fvec3 v0; | ||||||
|  |         t_fvec2 uv0; | ||||||
|  |         t_fvec3 v1; | ||||||
|  |         t_fvec2 uv1; | ||||||
|  |         t_fvec3 v2; | ||||||
|  |         t_fvec2 uv2; | ||||||
|  |     } payload; | ||||||
|  | }; | ||||||
|  |  | ||||||
| /* batch of primitives with overlapping properties */ | /* batch of primitives with overlapping properties */ | ||||||
| struct mesh_batch { | struct mesh_batch { | ||||||
|     GLuint buffer; /* server side storage */ |     GLuint buffer;          /* server side storage */ | ||||||
|     uint8_t *data; /* client side storage */ |     size_t buffer_len;      /* element count */ | ||||||
|     // size_t buffer_len; /* element count */ |     uint8_t *primitives; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| struct mesh_batch_item { | struct mesh_batch_item { | ||||||
|     char *key; |     t_texture_key key; | ||||||
|     struct mesh_batch value; |     struct mesh_batch value; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| /* is structure that is in opengl vertex array */ |  | ||||||
| struct uncolored_space_triangle { |  | ||||||
|     t_fvec3 v0; |  | ||||||
|     t_fvec2 uv0; |  | ||||||
|     t_fvec3 v1; |  | ||||||
|     t_fvec2 uv1; |  | ||||||
|     t_fvec3 v2; |  | ||||||
|     t_fvec2 uv2; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| #endif | #endif | ||||||
|   | |||||||
| @@ -1,6 +1,8 @@ | |||||||
| #ifndef PRIVATE_TEXTURES_H | #ifndef PRIVATE_TEXTURES_H | ||||||
| #define PRIVATE_TEXTURES_H | #define PRIVATE_TEXTURES_H | ||||||
|  |  | ||||||
|  | #include "../util.h" | ||||||
|  |  | ||||||
| #include <SDL2/SDL.h> | #include <SDL2/SDL.h> | ||||||
| #include <stb_rect_pack.h> | #include <stb_rect_pack.h> | ||||||
| #include <glad/glad.h> | #include <glad/glad.h> | ||||||
| @@ -8,10 +10,10 @@ | |||||||
| #include <stdbool.h> | #include <stdbool.h> | ||||||
|  |  | ||||||
| struct texture { | struct texture { | ||||||
|     SDL_Rect srcrect;           /* position in atlas */ |     t_rect srcrect;             /* position in atlas */ | ||||||
|     SDL_Surface *data;          /* original image data */ |     SDL_Surface *data;          /* original image data */ | ||||||
|     GLuint loner_data;          /* loner textures store their data directly */ |     int atlas_index; | ||||||
|     int atlas_index;            /* which atlas the texture is in */ |     GLuint loner_texture;       /* stored directly for loners, == 0 means atlas_index should be used*/ | ||||||
|     int8_t layer; |     int8_t layer; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| @@ -27,15 +29,14 @@ struct texture_cache { | |||||||
|     SDL_Window *window; |     SDL_Window *window; | ||||||
|  |  | ||||||
|     struct texture_cache_item *hash; |     struct texture_cache_item *hash; | ||||||
|     struct texture_cache_item *loner_hash; |  | ||||||
|  |  | ||||||
|     stbrp_node *node_buffer; /* used internally by stb_rect_pack */ |     stbrp_node *node_buffer;     /* used internally by stb_rect_pack */ | ||||||
|  |  | ||||||
|     SDL_Surface **atlas_surfaces; |     SDL_Surface **atlas_surfaces; | ||||||
|     GLuint *atlas_textures; |     GLuint *atlas_textures;      /* shared by atlas textures */ | ||||||
|     int atlas_index; |     int atlas_index; | ||||||
|  |  | ||||||
|     bool is_dirty;              /* current atlas needs to be recreated */ |     bool is_dirty;               /* current atlas needs to be recreated */ | ||||||
| }; | }; | ||||||
|  |  | ||||||
| #endif | #endif | ||||||
|   | |||||||
							
								
								
									
										216
									
								
								src/rendering.c
									
									
									
									
									
								
							
							
						
						
									
										216
									
								
								src/rendering.c
									
									
									
									
									
								
							| @@ -20,11 +20,8 @@ void render_queue_clear(void) { | |||||||
|     arrsetlen(ctx.render_queue_rectangles, 0); |     arrsetlen(ctx.render_queue_rectangles, 0); | ||||||
|     arrsetlen(ctx.render_queue_circles, 0); |     arrsetlen(ctx.render_queue_circles, 0); | ||||||
|  |  | ||||||
|     for (size_t i = 0; i < arrlenu(ctx.uncolored_mesh_batches); ++i) |     for (size_t i = 0; i < hmlenu(ctx.uncolored_mesh_batches); ++i) | ||||||
|         arrsetlen(ctx.uncolored_mesh_batches[i].data, 0); |         arrsetlen(ctx.uncolored_mesh_batches[i].value.primitives, 0); | ||||||
|  |  | ||||||
|     for (size_t i = 0; i < shlenu(ctx.uncolored_mesh_batches_loners); ++i) |  | ||||||
|         arrsetlen(ctx.uncolored_mesh_batches_loners[i].value.data, 0); |  | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -37,16 +34,12 @@ void render_queue_clear(void) { | |||||||
|  |  | ||||||
| /* sprite */ | /* sprite */ | ||||||
| void push_sprite(char *path, t_frect rect) { | void push_sprite(char *path, t_frect rect) { | ||||||
|     textures_load(&ctx.texture_cache, path); |  | ||||||
|  |  | ||||||
|     struct sprite_primitive sprite = { |     struct sprite_primitive sprite = { | ||||||
|         .rect = rect, |         .rect = rect, | ||||||
|         .color = (t_color) { 255, 255, 255, 255 }, |         .color = (t_color) { 255, 255, 255, 255 }, | ||||||
|         .path = path, |  | ||||||
|         .rotation = 0.0, |         .rotation = 0.0, | ||||||
|         .blend_mode = SDL_BLENDMODE_BLEND, |         .blend_mode = SDL_BLENDMODE_BLEND, | ||||||
|         .atlas_index = |         .texture_key = textures_get_key(&ctx.texture_cache, path), | ||||||
|             textures_get_atlas_index(&ctx.texture_cache, path), |  | ||||||
|         .layer = 0, |         .layer = 0, | ||||||
|         .flip_x = false, |         .flip_x = false, | ||||||
|         .flip_y = false, |         .flip_y = false, | ||||||
| @@ -57,16 +50,12 @@ void push_sprite(char *path, t_frect rect) { | |||||||
|  |  | ||||||
|  |  | ||||||
| void push_sprite_ex(t_frect rect, t_push_sprite_args args) { | void push_sprite_ex(t_frect rect, t_push_sprite_args args) { | ||||||
|     textures_load(&ctx.texture_cache, args.path); |  | ||||||
|  |  | ||||||
|     struct sprite_primitive sprite = { |     struct sprite_primitive sprite = { | ||||||
|         .rect = rect, |         .rect = rect, | ||||||
|         .color = args.color, |         .color = args.color, | ||||||
|         .path = args.path, |  | ||||||
|         .rotation = args.rotation, |         .rotation = args.rotation, | ||||||
|         .blend_mode = args.blend_mode, |         .blend_mode = args.blend_mode, | ||||||
|         .atlas_index =  |         .texture_key =  textures_get_key(&ctx.texture_cache, args.path), | ||||||
|             textures_get_atlas_index(&ctx.texture_cache, args.path), |  | ||||||
|         .layer = args.layer, |         .layer = args.layer, | ||||||
|         .flip_x = args.flip_x, |         .flip_x = args.flip_x, | ||||||
|         .flip_y = args.flip_y, |         .flip_y = args.flip_y, | ||||||
| @@ -100,6 +89,7 @@ void push_circle(t_fvec2 position, float radius, t_color color) { | |||||||
|  |  | ||||||
|  |  | ||||||
| /* TODO: automatic handling of repeating textures */ | /* TODO: automatic handling of repeating textures */ | ||||||
|  | /*       for that we could allocate a loner texture */ | ||||||
| void unfurl_triangle(const char *path, | void unfurl_triangle(const char *path, | ||||||
|                      t_fvec3 v0, |                      t_fvec3 v0, | ||||||
|                      t_fvec3 v1, |                      t_fvec3 v1, | ||||||
| @@ -108,70 +98,35 @@ void unfurl_triangle(const char *path, | |||||||
|                      t_shvec2 uv1, |                      t_shvec2 uv1, | ||||||
|                      t_shvec2 uv2) |                      t_shvec2 uv2) | ||||||
| { | { | ||||||
|     /* corrected atlas texture coordinates */ |     const t_texture_key texture_key = textures_get_key(&ctx.texture_cache, path); | ||||||
|     t_fvec2 uv0c, uv1c, uv2c; |  | ||||||
|     struct mesh_batch *batch_p; |  | ||||||
|  |  | ||||||
|     textures_load(&ctx.texture_cache, path); |     struct mesh_batch_item *batch_p = hmgetp_null(ctx.uncolored_mesh_batches, texture_key); | ||||||
|     const SDL_Rect srcrect = textures_get_srcrect(&ctx.texture_cache, path); |     if (!batch_p) { | ||||||
|  |         struct mesh_batch item = {0}; | ||||||
|     const int atlas_index = textures_get_atlas_index(&ctx.texture_cache, path); |         hmput(ctx.uncolored_mesh_batches, texture_key, item); | ||||||
|     if (atlas_index == -1) { |         batch_p = &ctx.uncolored_mesh_batches[hmlenu(ctx.uncolored_mesh_batches) - 1]; /* TODO: can last index be used? */ | ||||||
|         uv0c.x = (float)uv0.x / (float)srcrect.w; |  | ||||||
|         uv0c.y = (float)uv0.y / (float)srcrect.h; |  | ||||||
|         uv1c.x = (float)uv1.x / (float)srcrect.w; |  | ||||||
|         uv1c.y = (float)uv1.y / (float)srcrect.h; |  | ||||||
|         uv2c.x = (float)uv2.x / (float)srcrect.w; |  | ||||||
|         uv2c.y = (float)uv2.y / (float)srcrect.h; |  | ||||||
|  |  | ||||||
|         batch_p = &shgetp(ctx.uncolored_mesh_batches_loners, path)->value; |  | ||||||
|  |  | ||||||
|     } else { |  | ||||||
|         const size_t old_len = arrlenu(ctx.uncolored_mesh_batches); |  | ||||||
|         if ((size_t)atlas_index + 1 >= old_len) { |  | ||||||
|             /* grow to accommodate texture cache atlases */ |  | ||||||
|             arrsetlen(ctx.uncolored_mesh_batches, atlas_index + 1); |  | ||||||
|  |  | ||||||
|             /* zero initialize it all, it's a valid state */ |  | ||||||
|             SDL_memset(&ctx.uncolored_mesh_batches[atlas_index], |  | ||||||
|                        0, |  | ||||||
|                        sizeof (struct mesh_batch) * ((atlas_index + 1) - old_len)); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         const float wr = (float)srcrect.w / TEXTURE_ATLAS_SIZE; |  | ||||||
|         const float hr = (float)srcrect.h / TEXTURE_ATLAS_SIZE; |  | ||||||
|         const float xr = (float)srcrect.x / TEXTURE_ATLAS_SIZE; |  | ||||||
|         const float yr = (float)srcrect.y / TEXTURE_ATLAS_SIZE; |  | ||||||
|  |  | ||||||
|         uv0c.x = xr + ((float)uv0.x / (float)srcrect.w) * wr; |  | ||||||
|         uv0c.y = yr + ((float)uv0.y / (float)srcrect.h) * hr; |  | ||||||
|         uv1c.x = xr + ((float)uv1.x / (float)srcrect.w) * wr; |  | ||||||
|         uv1c.y = yr + ((float)uv1.y / (float)srcrect.h) * hr; |  | ||||||
|         uv2c.x = xr + ((float)uv2.x / (float)srcrect.w) * wr; |  | ||||||
|         uv2c.y = yr + ((float)uv2.y / (float)srcrect.h) * hr; |  | ||||||
|  |  | ||||||
|         batch_p = &ctx.uncolored_mesh_batches[atlas_index]; |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     struct uncolored_space_triangle *data = (struct uncolored_space_triangle *)batch_p->data; |     union uncolored_space_triangle triangle = { .primitive = { | ||||||
|     struct uncolored_space_triangle pack = { |  | ||||||
|         .v0 = v0, |         .v0 = v0, | ||||||
|         .uv0 = uv0c, |  | ||||||
|         .v1 = v1, |         .v1 = v1, | ||||||
|         .uv1 = uv1c, |  | ||||||
|         .v2 = v2, |         .v2 = v2, | ||||||
|         .uv2 = uv2c, |         .uv1 = m_to_fvec2(uv1), | ||||||
|     }; |         .uv0 = m_to_fvec2(uv0), | ||||||
|     arrpush(data, pack); |         .uv2 = m_to_fvec2(uv2), | ||||||
|  |     }}; | ||||||
|  |  | ||||||
|     batch_p->data = (uint8_t *)data; |     union uncolored_space_triangle *triangles = | ||||||
|  |         (union uncolored_space_triangle *)batch_p->value.primitives; | ||||||
|  |     arrpush(triangles, triangle); | ||||||
|  |     batch_p->value.primitives = (uint8_t *)triangles; | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| /* compare functions for the sort in render_sprites */ | /* compare functions for the sort in render_sprites */ | ||||||
| static int cmp_atlases(const void *a, const void *b) { | static int cmp_atlases(const void *a, const void *b) { | ||||||
|     int index_a = ((const struct sprite_primitive *)a)->atlas_index; |     int index_a = ((const struct sprite_primitive *)a)->texture_key.id; | ||||||
|     int index_b = ((const struct sprite_primitive *)b)->atlas_index; |     int index_b = ((const struct sprite_primitive *)b)->texture_key.id; | ||||||
|  |  | ||||||
|     return (index_a > index_b) - (index_a < index_b); |     return (index_a > index_b) - (index_a < index_b); | ||||||
| } | } | ||||||
| @@ -259,40 +214,28 @@ static void render_sprite(struct sprite_primitive *sprite) { | |||||||
|     glEnableClientState(GL_TEXTURE_COORD_ARRAY); |     glEnableClientState(GL_TEXTURE_COORD_ARRAY); | ||||||
|     glClientActiveTexture(GL_TEXTURE0); |     glClientActiveTexture(GL_TEXTURE0); | ||||||
|  |  | ||||||
|     /* loner */ |     textures_bind(&ctx.texture_cache, sprite->texture_key, GL_TEXTURE_2D); | ||||||
|     if (sprite->atlas_index == -1) { |  | ||||||
|         textures_bind_loner(&ctx.texture_cache, sprite->path, GL_TEXTURE_2D); |     t_rect srcrect = textures_get_srcrect(&ctx.texture_cache, sprite->texture_key); | ||||||
|         glTexCoordPointer(2, |     t_rect dims    = textures_get_dims(&ctx.texture_cache, sprite->texture_key); | ||||||
|                           GL_SHORT, |  | ||||||
|                           0, |     glTexCoordPointer(2, | ||||||
|                           (void *)(int16_t[6 * 2]) { |                       GL_FLOAT, | ||||||
|                             0,         INT16_MAX, |                       0, | ||||||
|                             0,         0, |                       /* TODO: try using shorts */ | ||||||
|                             INT16_MAX, 0, |                       (void *)(float[6 * 2]) { | ||||||
|                             INT16_MAX, 0, |                         (float)srcrect.x               / (float)dims.w, | ||||||
|                             INT16_MAX, INT16_MAX, |                         (float)srcrect.y               / (float)dims.h, | ||||||
|                             0,         INT16_MAX }); |                         (float)srcrect.x               / (float)dims.w, | ||||||
|     } else { |                         (float)(srcrect.y + srcrect.h) / (float)dims.h, | ||||||
|         SDL_Rect srcrect = textures_get_srcrect(&ctx.texture_cache, sprite->path); |                         (float)(srcrect.x + srcrect.w) / (float)dims.w, | ||||||
|         textures_bind_atlas(&ctx.texture_cache, sprite->atlas_index, GL_TEXTURE_2D); |                         (float)(srcrect.y + srcrect.h) / (float)dims.h, | ||||||
|         glTexCoordPointer(2, |                         (float)(srcrect.x + srcrect.w) / (float)dims.w, | ||||||
|                           GL_FLOAT, |                         (float)(srcrect.y + srcrect.h) / (float)dims.h, | ||||||
|                           0, |                         (float)(srcrect.x + srcrect.w) / (float)dims.w, | ||||||
|                           /* TODO: try using shorts */ |                         (float)srcrect.y               / (float)dims.h, | ||||||
|                           (void *)(float[6 * 2]) { |                         (float)srcrect.x               / (float)dims.w, | ||||||
|                             (float)srcrect.x  / TEXTURE_ATLAS_SIZE, |                         (float)srcrect.y               / (float)dims.h }); | ||||||
|                             (float)srcrect.y  / TEXTURE_ATLAS_SIZE, |  | ||||||
|                             (float)srcrect.x  / TEXTURE_ATLAS_SIZE, |  | ||||||
|                             (float)(srcrect.y + srcrect.h) / TEXTURE_ATLAS_SIZE, |  | ||||||
|                             (float)(srcrect.x + srcrect.w) / TEXTURE_ATLAS_SIZE, |  | ||||||
|                             (float)(srcrect.y + srcrect.h) / TEXTURE_ATLAS_SIZE, |  | ||||||
|                             (float)(srcrect.x + srcrect.w) / TEXTURE_ATLAS_SIZE, |  | ||||||
|                             (float)(srcrect.y + srcrect.h) / TEXTURE_ATLAS_SIZE, |  | ||||||
|                             (float)(srcrect.x + srcrect.w) / TEXTURE_ATLAS_SIZE, |  | ||||||
|                             (float)srcrect.y  / TEXTURE_ATLAS_SIZE, |  | ||||||
|                             (float)srcrect.x  / TEXTURE_ATLAS_SIZE, |  | ||||||
|                             (float)srcrect.y  / TEXTURE_ATLAS_SIZE }); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     glColor4ub(sprite->color.r, sprite->color.g, sprite->color.b, sprite->color.a); |     glColor4ub(sprite->color.r, sprite->color.g, sprite->color.b, sprite->color.a); | ||||||
|  |  | ||||||
| @@ -446,41 +389,69 @@ static void render_circles(void) { | |||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| static void draw_uncolored_space_traingle_batch(struct mesh_batch *batch) { | static void draw_uncolored_space_traingle_batch(struct mesh_batch *batch, | ||||||
|     size_t data_len = arrlenu(batch->data); |                                                 t_texture_key texture_key) | ||||||
|  | { | ||||||
|  |     size_t primitives_len = arrlenu(batch->primitives); | ||||||
|  |  | ||||||
|  |     if (primitives_len == 0) | ||||||
|  |         return; | ||||||
|  |  | ||||||
|     /* create vertex array object */ |     /* create vertex array object */ | ||||||
|     if (batch->buffer == 0) |     if (batch->buffer == 0) | ||||||
|         glGenBuffers(1, &batch->buffer); |         glGenBuffers(1, &batch->buffer); | ||||||
|  |  | ||||||
|     glBindBuffer(GL_ARRAY_BUFFER, batch->buffer); |  | ||||||
|  |  | ||||||
|     /* TODO: try using mapped buffers while building batches instead? */ |     /* TODO: try using mapped buffers while building batches instead? */ | ||||||
|     /*       this way we could skip client side copy that is kept until commitment */ |     /*       this way we could skip client side copy that is kept until commitment */ | ||||||
|     /*       alternatively we could commit glBufferSubData based on a threshold */ |     /*       alternatively we could commit glBufferSubData based on a threshold */ | ||||||
|  |  | ||||||
|  |     /* update pixel-based uvs to correspond with texture atlases */ | ||||||
|  |     for (size_t i = 0; i < primitives_len; ++i) { | ||||||
|  |         struct uncolored_space_triangle_payload *payload = | ||||||
|  |             &((union uncolored_space_triangle *)batch->primitives)[i].payload; | ||||||
|  |  | ||||||
|  |         t_rect srcrect = textures_get_srcrect(&ctx.texture_cache, texture_key); | ||||||
|  |         t_rect dims = textures_get_dims(&ctx.texture_cache, texture_key); | ||||||
|  |  | ||||||
|  |         const float wr = (float)srcrect.w / (float)dims.w; | ||||||
|  |         const float hr = (float)srcrect.h / (float)dims.h; | ||||||
|  |         const float xr = (float)srcrect.x / (float)dims.w; | ||||||
|  |         const float yr = (float)srcrect.y / (float)dims.h; | ||||||
|  |  | ||||||
|  |         payload->uv0.x = xr + ((float)payload->uv0.x / (float)srcrect.w) * wr; | ||||||
|  |         payload->uv0.y = yr + ((float)payload->uv0.y / (float)srcrect.h) * hr; | ||||||
|  |         payload->uv1.x = xr + ((float)payload->uv1.x / (float)srcrect.w) * wr; | ||||||
|  |         payload->uv1.y = yr + ((float)payload->uv1.y / (float)srcrect.h) * hr; | ||||||
|  |         payload->uv2.x = xr + ((float)payload->uv2.x / (float)srcrect.w) * wr; | ||||||
|  |         payload->uv2.y = yr + ((float)payload->uv2.y / (float)srcrect.h) * hr; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     textures_bind(&ctx.texture_cache, texture_key, GL_TEXTURE_2D); | ||||||
|  |  | ||||||
|  |     glBindBuffer(GL_ARRAY_BUFFER, batch->buffer); | ||||||
|  |  | ||||||
|     /* upload batched data */ |     /* upload batched data */ | ||||||
|     glBufferData(GL_ARRAY_BUFFER, |     glBufferData(GL_ARRAY_BUFFER, | ||||||
|                  data_len * sizeof (struct uncolored_space_triangle), |                  primitives_len * sizeof (struct uncolored_space_triangle_payload), | ||||||
|                  batch->data, |                  batch->primitives, | ||||||
|                  GL_STREAM_DRAW); |                  GL_STREAM_DRAW); | ||||||
|  |  | ||||||
|     /* vertex specification*/ |     /* vertex specification*/ | ||||||
|     glEnableClientState(GL_VERTEX_ARRAY); |     glEnableClientState(GL_VERTEX_ARRAY); | ||||||
|     glVertexPointer(3, |     glVertexPointer(3, | ||||||
|                     GL_FLOAT, |                     GL_FLOAT, | ||||||
|                     offsetof(struct uncolored_space_triangle, v1), |                     offsetof(struct uncolored_space_triangle_payload, v1), | ||||||
|                     (void *)offsetof(struct uncolored_space_triangle, v0)); |                     (void *)offsetof(struct uncolored_space_triangle_payload, v0)); | ||||||
|  |  | ||||||
|     glEnableClientState(GL_TEXTURE_COORD_ARRAY); |     glEnableClientState(GL_TEXTURE_COORD_ARRAY); | ||||||
|     glClientActiveTexture(GL_TEXTURE0); |     glClientActiveTexture(GL_TEXTURE0); | ||||||
|     glTexCoordPointer(2, |     glTexCoordPointer(2, | ||||||
|                       GL_FLOAT, |                       GL_FLOAT, | ||||||
|                       offsetof(struct uncolored_space_triangle, v1), |                       offsetof(struct uncolored_space_triangle_payload, v1), | ||||||
|                       (void *)offsetof(struct uncolored_space_triangle, uv0)); |                       (void *)offsetof(struct uncolored_space_triangle_payload, uv0)); | ||||||
|  |  | ||||||
|     /* commit for drawing */ |     /* commit for drawing */ | ||||||
|     glDrawArrays(GL_TRIANGLES, 0, 3 * (int)data_len); |     glDrawArrays(GL_TRIANGLES, 0, 3 * (int)primitives_len); | ||||||
|  |  | ||||||
|     glDisableClientState(GL_TEXTURE_COORD_ARRAY); |     glDisableClientState(GL_TEXTURE_COORD_ARRAY); | ||||||
|     glDisableClientState(GL_VERTEX_ARRAY); |     glDisableClientState(GL_VERTEX_ARRAY); | ||||||
| @@ -498,28 +469,15 @@ static void render_space(void) { | |||||||
|     /* solid white, no modulation */ |     /* solid white, no modulation */ | ||||||
|     glColor4f(1.0f, 1.0f, 1.0f, 1.0f); |     glColor4f(1.0f, 1.0f, 1.0f, 1.0f); | ||||||
|  |  | ||||||
|     for (size_t i = 0; i < arrlenu(ctx.uncolored_mesh_batches); ++i) { |     for (size_t i = 0; i < hmlenu(ctx.uncolored_mesh_batches); ++i) { | ||||||
|         if (arrlenu(&ctx.uncolored_mesh_batches[i].data) > 0) { |         draw_uncolored_space_traingle_batch(&ctx.uncolored_mesh_batches[i].value, | ||||||
|             textures_bind_atlas(&ctx.texture_cache, (int)i, GL_TEXTURE_2D); |                                             ctx.uncolored_mesh_batches[i].key); | ||||||
|             draw_uncolored_space_traingle_batch(&ctx.uncolored_mesh_batches[i]); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     for (size_t i = 0; i < shlenu(ctx.uncolored_mesh_batches_loners); ++i) { |  | ||||||
|         if (arrlenu(&ctx.uncolored_mesh_batches_loners[i].value.data) > 0) { |  | ||||||
|             textures_bind_loner(&ctx.texture_cache,  |  | ||||||
|                                 ctx.uncolored_mesh_batches_loners[i].key, |  | ||||||
|                                 GL_TEXTURE_2D); |  | ||||||
|  |  | ||||||
|             draw_uncolored_space_traingle_batch(&ctx.uncolored_mesh_batches_loners[i].value); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| void render(void) { | void render(void) { | ||||||
|     if (ctx.texture_cache.is_dirty) |     textures_update_atlas(&ctx.texture_cache); | ||||||
|         textures_update_current_atlas(&ctx.texture_cache); |  | ||||||
|  |  | ||||||
|     /* fit rendering context onto the resizable screen */ |     /* fit rendering context onto the resizable screen */ | ||||||
|     if ((float)ctx.window_w / (float)ctx.window_h > RENDER_BASE_RATIO) { |     if ((float)ctx.window_w / (float)ctx.window_h > RENDER_BASE_RATIO) { | ||||||
|   | |||||||
							
								
								
									
										136
									
								
								src/textures.c
									
									
									
									
									
								
							
							
						
						
									
										136
									
								
								src/textures.c
									
									
									
									
									
								
							| @@ -10,7 +10,6 @@ | |||||||
| #include <stb_ds.h> | #include <stb_ds.h> | ||||||
| #include <stb_rect_pack.h> | #include <stb_rect_pack.h> | ||||||
|  |  | ||||||
| #include <limits.h> |  | ||||||
| #include <stdbool.h> | #include <stdbool.h> | ||||||
| #include <stdlib.h> | #include <stdlib.h> | ||||||
| #include <stdio.h> | #include <stdio.h> | ||||||
| @@ -139,7 +138,12 @@ static void recreate_current_atlas_texture(struct texture_cache *cache) { | |||||||
|         SDL_BlitSurface(cache->hash[i].value.data, |         SDL_BlitSurface(cache->hash[i].value.data, | ||||||
|                         NULL, |                         NULL, | ||||||
|                         atlas_surface, |                         atlas_surface, | ||||||
|                         &cache->hash[i].value.srcrect); |                         &(SDL_Rect){ | ||||||
|  |                             .x = cache->hash[i].value.srcrect.x, | ||||||
|  |                             .y = cache->hash[i].value.srcrect.y, | ||||||
|  |                             .w = cache->hash[i].value.srcrect.w, | ||||||
|  |                             .h = cache->hash[i].value.srcrect.h, | ||||||
|  |                         }); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* texturize it! */ |     /* texturize it! */ | ||||||
| @@ -212,7 +216,7 @@ static bool update_rects(struct texture_cache *cache, stbrp_rect *rects, stbrp_r | |||||||
| /* updates the atlas location of every rect in the cache */ | /* updates the atlas location of every rect in the cache */ | ||||||
| static void update_texture_rects_in_atlas(struct texture_cache *cache, stbrp_rect *rects) { | static void update_texture_rects_in_atlas(struct texture_cache *cache, stbrp_rect *rects) { | ||||||
|     for (size_t i = 0; i < arrlenu(rects); ++i) { |     for (size_t i = 0; i < arrlenu(rects); ++i) { | ||||||
|         cache->hash[i].value.srcrect = (SDL_Rect) { |         cache->hash[i].value.srcrect = (t_rect) { | ||||||
|             .x = rects[i].x, |             .x = rects[i].x, | ||||||
|             .y = rects[i].y, |             .y = rects[i].y, | ||||||
|             .w = rects[i].w, |             .w = rects[i].w, | ||||||
| @@ -225,7 +229,6 @@ static void update_texture_rects_in_atlas(struct texture_cache *cache, stbrp_rec | |||||||
| void textures_cache_init(struct texture_cache *cache, SDL_Window *window) { | void textures_cache_init(struct texture_cache *cache, SDL_Window *window) { | ||||||
|     cache->window = window; |     cache->window = window; | ||||||
|     sh_new_arena(cache->hash); |     sh_new_arena(cache->hash); | ||||||
|     sh_new_arena(cache->loner_hash); |  | ||||||
|  |  | ||||||
|     cache->node_buffer = cmalloc(sizeof *cache->node_buffer * TEXTURE_ATLAS_SIZE); |     cache->node_buffer = cmalloc(sizeof *cache->node_buffer * TEXTURE_ATLAS_SIZE); | ||||||
|  |  | ||||||
| @@ -253,11 +256,6 @@ void textures_cache_deinit(struct texture_cache *cache) { | |||||||
|     } |     } | ||||||
|     shfree(cache->hash); |     shfree(cache->hash); | ||||||
|  |  | ||||||
|     for (size_t i = 0; i < shlenu(cache->loner_hash); ++i) { |  | ||||||
|         SDL_FreeSurface(cache->loner_hash[i].value.data); |  | ||||||
|     } |  | ||||||
|     shfree(cache->loner_hash); |  | ||||||
|  |  | ||||||
|     free(cache->node_buffer); |     free(cache->node_buffer); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -282,38 +280,38 @@ void textures_dump_atlases(struct texture_cache *cache) { | |||||||
|         IMG_SavePNG_RW(cache->atlas_surfaces[i], handle, true); |         IMG_SavePNG_RW(cache->atlas_surfaces[i], handle, true); | ||||||
|         log_info("Dumped atlas %s", buf); |         log_info("Dumped atlas %s", buf); | ||||||
|     } |     } | ||||||
|      |  | ||||||
|     size_t num_loners = shlenu(cache->loner_hash); |  | ||||||
|     log_info("%zd atlases dumped. %zd loners left undumped.", i, num_loners); |  | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| void textures_load(struct texture_cache *cache, const char *path) { | static t_texture_key textures_load(struct texture_cache *cache, const char *path) { | ||||||
|     /* no need to do anything if it was loaded already */ |     /* no need to do anything if it was loaded already */ | ||||||
|     if (shgeti(cache->hash, path) >= 0 || shgeti(cache->loner_hash, path) >= 0) |     if (shgeti(cache->hash, path) >= 0) | ||||||
|         return; |         return (t_texture_key){0}; | ||||||
|  |  | ||||||
|     SDL_Surface *surface = image_to_surface(path); |     SDL_Surface *surface = image_to_surface(path); | ||||||
|     struct texture new_texture; |     struct texture new_texture = {0}; | ||||||
|     new_texture.data = surface; |     new_texture.data = surface; | ||||||
|  |  | ||||||
|     /* it's a "loner texture," it doesn't fit in an atlas so it's not in one */ |     /* it's a "loner texture," it doesn't fit in an atlas so it's not in one */ | ||||||
|     if (surface->w > TEXTURE_ATLAS_SIZE || surface->h > TEXTURE_ATLAS_SIZE) { |     if (surface->w > TEXTURE_ATLAS_SIZE || surface->h > TEXTURE_ATLAS_SIZE) { | ||||||
|         new_texture.loner_data = new_gl_texture(); |         new_texture.loner_texture = new_gl_texture(); | ||||||
|         upload_texture_from_surface(new_texture.loner_data, surface);  |         upload_texture_from_surface(new_texture.loner_texture, surface);  | ||||||
|         new_texture.atlas_index = -1; |         new_texture.srcrect = (t_rect) { .w = surface->w, .h = surface->h }; | ||||||
|         new_texture.srcrect = (SDL_Rect) { |         shput(cache->hash, path, new_texture); | ||||||
|             .w = surface->w, .h = surface->h }; |         return (t_texture_key){ (int)shgeti(cache->hash, path) + 1 }; | ||||||
|         shput(cache->loner_hash, path, new_texture); |  | ||||||
|     } else { |     } else { | ||||||
|         new_texture.atlas_index = cache->atlas_index; |         new_texture.atlas_index = cache->atlas_index; | ||||||
|         shput(cache->hash, path, new_texture); |         shput(cache->hash, path, new_texture); | ||||||
|         cache->is_dirty = true; |         cache->is_dirty = true; | ||||||
|  |         return (t_texture_key){ (int)shgeti(cache->hash, path) + 1 }; | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| void textures_update_current_atlas(struct texture_cache *cache) { | void textures_update_atlas(struct texture_cache *cache) { | ||||||
|  |     if (!cache->is_dirty) | ||||||
|  |         return; | ||||||
|  |  | ||||||
|     /* this function makes a lot more sense if you read stb_rect_pack.h */ |     /* this function makes a lot more sense if you read stb_rect_pack.h */ | ||||||
|     stbrp_context pack_ctx; /* target info */ |     stbrp_context pack_ctx; /* target info */ | ||||||
|     stbrp_init_target(&pack_ctx, |     stbrp_init_target(&pack_ctx, | ||||||
| @@ -355,68 +353,56 @@ void textures_update_current_atlas(struct texture_cache *cache) { | |||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| SDL_Rect textures_get_srcrect(struct texture_cache *cache, const char *path) { | t_texture_key textures_get_key(struct texture_cache *cache, const char *path) { | ||||||
|     int index = textures_get_atlas_index(cache, path); |     /* hash tables are assumed to be stable, so we just return indices */ | ||||||
|     if (index == -1) { |     ptrdiff_t texture = shgeti(cache->hash, path); | ||||||
|         return shget(cache->loner_hash, path).srcrect; |  | ||||||
|     } else if (index == INT_MIN) { |     /* load it if it isn't */ | ||||||
|  |     if (texture == -1) { | ||||||
|  |         return textures_load(cache, path); | ||||||
|  |     } else | ||||||
|  |         return (t_texture_key){ (int)texture + 1 }; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | t_rect textures_get_srcrect(const struct texture_cache *cache, t_texture_key key) { | ||||||
|  |     if (key.id != 0) { | ||||||
|  |         return cache->hash[key.id - 1].value.srcrect; | ||||||
|  |     } else { | ||||||
|         CRY("Texture lookup failed.", |         CRY("Texture lookup failed.", | ||||||
|             "Tried to get texture that isn't loaded."); |             "Tried to get texture that isn't loaded."); | ||||||
|         return (SDL_Rect){ 0, 0, 0, 0 }; |         return (t_rect){ 0, 0, 0, 0 }; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | t_rect textures_get_dims(const struct texture_cache *cache, t_texture_key key) { | ||||||
|  |     if (key.id != 0) { | ||||||
|  |         if (cache->hash[key.id - 1].value.loner_texture != 0) | ||||||
|  |             return cache->hash[key.id - 1].value.srcrect; | ||||||
|  |         else | ||||||
|  |             return (t_rect){ .w = TEXTURE_ATLAS_SIZE, .h = TEXTURE_ATLAS_SIZE }; | ||||||
|     } else { |     } else { | ||||||
|         return shget(cache->hash, path).srcrect; |         CRY("Texture lookup failed.", | ||||||
|  |             "Tried to get texture that isn't loaded."); | ||||||
|  |         return (t_rect){ 0, 0, 0, 0 }; | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| /* TODO: instead of index, return a 'key' with following encoded meaning: */ | void textures_bind(const struct texture_cache *cache, t_texture_key key, GLenum target) { | ||||||
| /*       value of 0 - no atlas (#define NO_ATLAS (0) ?) */ |     if (key.id != 0) { | ||||||
| /*       negative value - index in loners (-key - 1) */ |         if (cache->hash[key.id - 1].value.loner_texture == 0) | ||||||
| /*       positive value - index in atlases (key - 1) */ |             glBindTexture(target, cache->atlas_textures[cache->hash[key.id - 1].value.atlas_index]); | ||||||
| int textures_get_atlas_index(struct texture_cache *cache, const char *path) { |         else | ||||||
|     struct texture_cache_item *texture = shgetp_null(cache->hash, path); |             glBindTexture(target, cache->hash[key.id - 1].value.loner_texture); | ||||||
|  |     } else if (key.id == 0) { | ||||||
|     /* it might be a loner texture */ |         CRY("Texture binding failed.", | ||||||
|     if (texture == NULL) { |             "Tried to get texture that isn't loaded."); | ||||||
|         texture = shgetp_null(cache->loner_hash, path); |  | ||||||
|  |  | ||||||
|         /* never mind it's just not there at all */ |  | ||||||
|         if (texture == NULL) { |  | ||||||
|             CRY("Texture atlas index lookup failed.", |  | ||||||
|                 "Tried to get atlas index of texture that isn't loaded."); |  | ||||||
|             return INT_MIN; |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return texture->value.atlas_index; |  | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| void textures_bind_atlas(struct texture_cache *cache, int index, GLenum target) { | size_t textures_get_num_atlases(const struct texture_cache *cache) { | ||||||
|     /* out of bounds */ |  | ||||||
|     if (arrlen(cache->atlas_textures) < index + 1 || index < 0) { |  | ||||||
|         CRY("Atlas texture binding failed.", |  | ||||||
|             "Tried to bind texture by invalid index"); |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     glBindTexture(target, cache->atlas_textures[index]); |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| void textures_bind_loner(struct texture_cache *cache, const char *path, GLenum target) { |  | ||||||
|     struct texture_cache_item *texture = shgetp_null(cache->loner_hash, path); |  | ||||||
|  |  | ||||||
|     if (texture == NULL) { |  | ||||||
|         CRY("Loner texture binding failed.", |  | ||||||
|             "Tried to bind texture that isn't loaded."); |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     glBindTexture(target, texture->value.loner_data); |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| size_t textures_get_num_atlases(struct texture_cache *cache) { |  | ||||||
|     return cache->atlas_index + 1;     |     return cache->atlas_index + 1;     | ||||||
| } | } | ||||||
|   | |||||||
| @@ -2,10 +2,20 @@ | |||||||
| #define TEXTURES_H | #define TEXTURES_H | ||||||
|  |  | ||||||
| #include "private/textures.h" | #include "private/textures.h" | ||||||
|  | #include "util.h" | ||||||
|  |  | ||||||
| #include <SDL2/SDL.h> | #include <SDL2/SDL.h> | ||||||
| #include <glad/glad.h> | #include <glad/glad.h> | ||||||
|  |  | ||||||
|  | /* type safe structure for persistent texture handles */ | ||||||
|  | typedef struct { int id; } t_texture_key; | ||||||
|  |  | ||||||
|  | /* tests whether given key structure corresponds to any texture */ | ||||||
|  | #define m_texture_key_is_valid(p_key) ((p_key).id != 0) | ||||||
|  |  | ||||||
|  | /* tests whether given key corresponds to atlas texture or a loner */ | ||||||
|  | #define m_texture_is_atlas(p_key) ((p_key).id > 0) | ||||||
|  |  | ||||||
| void textures_cache_init(struct texture_cache *cache, SDL_Window *window); | void textures_cache_init(struct texture_cache *cache, SDL_Window *window); | ||||||
| void textures_cache_deinit(struct texture_cache *cache); | void textures_cache_deinit(struct texture_cache *cache); | ||||||
|  |  | ||||||
| @@ -15,25 +25,27 @@ void textures_dump_atlases(struct texture_cache *cache); | |||||||
| /* loads an image if it isn't in the cache, otherwise a no-op. */ | /* loads an image if it isn't in the cache, otherwise a no-op. */ | ||||||
| /* can be called from anywhere at any time after init, useful if you want to */ | /* can be called from anywhere at any time after init, useful if you want to */ | ||||||
| /* preload textures you know will definitely be used */ | /* preload textures you know will definitely be used */ | ||||||
| void textures_load(struct texture_cache *cache, const char *path); | // void textures_load(struct texture_cache *cache, const char *path); | ||||||
|  |  | ||||||
| /* repacks the current texture atlas based on the texture cache */ | /* repacks the current texture atlas based on the texture cache if needed */ | ||||||
| void textures_update_current_atlas(struct texture_cache *cache); | /* any previously returned srcrect results are invalidated after that */ | ||||||
|  | /* call it every time before rendering */ | ||||||
|  | void textures_update_atlas(struct texture_cache *cache); | ||||||
|  |  | ||||||
| /* returns a rect in a texture cache atlas based on a path, for drawing */ | /* returns a persistent handle to some texture in cache, loading it if needed */ | ||||||
| /* if the texture is not found, returns a zero-filled rect (so check w or h) */ | /* check the result with m_texture_key_is_valid() */ | ||||||
| SDL_Rect textures_get_srcrect(struct texture_cache *cache, const char *path); | t_texture_key textures_get_key(struct texture_cache *cache, const char *path); | ||||||
|  |  | ||||||
| /* returns which atlas the texture in the path is in, starting from 0 */ | /* returns a rect in a texture cache of the given key */ | ||||||
| /* if the texture is not found, returns INT_MIN */ | t_rect textures_get_srcrect(const struct texture_cache *cache, t_texture_key key); | ||||||
| int textures_get_atlas_index(struct texture_cache *cache, const char *path); |  | ||||||
|  | /* returns a rect of dimensions of the whole texture (whole atlas) */ | ||||||
|  | t_rect textures_get_dims(const struct texture_cache *cache, t_texture_key key); | ||||||
|  |  | ||||||
| /* binds atlas texture in opengl state */ | /* binds atlas texture in opengl state */ | ||||||
| void textures_bind_atlas(struct texture_cache *cache, int index, GLenum target); | void textures_bind(const struct texture_cache *cache, t_texture_key key, GLenum target); | ||||||
|  |  | ||||||
| void textures_bind_loner(struct texture_cache *cache, const char *path, GLenum target); |  | ||||||
|  |  | ||||||
| /* returns the number of atlases in the cache */ | /* returns the number of atlases in the cache */ | ||||||
| size_t textures_get_num_atlases(struct texture_cache *cache); | size_t textures_get_num_atlases(const struct texture_cache *cache); | ||||||
|  |  | ||||||
| #endif | #endif | ||||||
|   | |||||||
							
								
								
									
										15
									
								
								src/util.c
									
									
									
									
									
								
							
							
						
						
									
										15
									
								
								src/util.c
									
									
									
									
									
								
							| @@ -186,6 +186,21 @@ t_frect to_frect(t_rect rect) { | |||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | t_fvec2 fvec2_from_vec2(t_vec2 vec) { | ||||||
|  |     return (t_fvec2) { | ||||||
|  |         .x = (float)vec.x, | ||||||
|  |         .y = (float)vec.y, | ||||||
|  |     }; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | t_fvec2 fvec2_from_shvec2(t_shvec2 vec) { | ||||||
|  |     return (t_fvec2) { | ||||||
|  |         .x = (float)vec.x, | ||||||
|  |         .y = (float)vec.y, | ||||||
|  |     }; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
| void tick_timer(int *value) { | void tick_timer(int *value) { | ||||||
|     *value = MAX(*value - 1, 0); |     *value = MAX(*value - 1, 0); | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										15
									
								
								src/util.h
									
									
									
									
									
								
							
							
						
						
									
										15
									
								
								src/util.h
									
									
									
									
									
								
							| @@ -48,7 +48,9 @@ void *ccalloc(size_t num, size_t size); | |||||||
| #define MAX SDL_max | #define MAX SDL_max | ||||||
| #define MIN SDL_min | #define MIN SDL_min | ||||||
|  |  | ||||||
|  | #ifndef M_PI | ||||||
| #define M_PI 3.14159265358979323846264338327950288   /**< pi */ | #define M_PI 3.14159265358979323846264338327950288   /**< pi */ | ||||||
|  | #endif | ||||||
|  |  | ||||||
| /* sets buf_out to a pointer to a byte buffer which must be freed. */ | /* sets buf_out to a pointer to a byte buffer which must be freed. */ | ||||||
| /* returns the size of this buffer. */ | /* returns the size of this buffer. */ | ||||||
| @@ -94,6 +96,7 @@ typedef struct frect { | |||||||
|  |  | ||||||
| bool intersect_frect(const t_frect *a, const t_frect *b, t_frect *result); | bool intersect_frect(const t_frect *a, const t_frect *b, t_frect *result); | ||||||
|  |  | ||||||
|  | /* TODO: generics and specials (see m_to_fvec2() for an example)*/ | ||||||
| t_frect to_frect(t_rect rect); | t_frect to_frect(t_rect rect); | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -122,6 +125,17 @@ typedef struct shvec2 { | |||||||
| } t_shvec2; | } t_shvec2; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* aren't macros to prevent double evaluation with side effects */ | ||||||
|  | /* maybe could be inlined? i hope LTO will resolve this */ | ||||||
|  | t_fvec2 fvec2_from_vec2(t_vec2 vec); | ||||||
|  | t_fvec2 fvec2_from_shvec2(t_shvec2 vec); | ||||||
|  |  | ||||||
|  | #define m_to_fvec2(p_any_vec2) (_Generic((p_any_vec2),          \ | ||||||
|  |                                     t_vec2:   fvec2_from_vec2,  \ | ||||||
|  |                                     t_shvec2: fvec2_from_shvec2 \ | ||||||
|  |                                 )(p_any_vec2)) | ||||||
|  |  | ||||||
|  |  | ||||||
| /* decrements an lvalue (which should be an int), stopping at 0 */ | /* decrements an lvalue (which should be an int), stopping at 0 */ | ||||||
| /* meant for tick-based timers in game logic */ | /* meant for tick-based timers in game logic */ | ||||||
| /* | /* | ||||||
| @@ -130,7 +144,6 @@ typedef struct shvec2 { | |||||||
|  */ |  */ | ||||||
| void tick_timer(int *value); | void tick_timer(int *value); | ||||||
|  |  | ||||||
|  |  | ||||||
| /* decrements a floating point second-based timer, stopping at 0.0 */ | /* decrements a floating point second-based timer, stopping at 0.0 */ | ||||||
| /* meant for poll based real time logic in game logic */ | /* meant for poll based real time logic in game logic */ | ||||||
| /* note that it should be decremented only on the next tick after its creation */ | /* note that it should be decremented only on the next tick after its creation */ | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user