twn_textures.c: fix atlas packing, allow out of order population
This commit is contained in:
		| @@ -255,17 +255,17 @@ static void upload_texture_from_surface(GPUTexture texture, SDL_Surface *surface | ||||
| } | ||||
|  | ||||
|  | ||||
| static void recreate_current_atlas_texture(TextureCache *cache) { | ||||
| static void recreate_current_atlas_texture(TextureCache *cache, int atlas_index) { | ||||
|     profile_start("atlas recreation"); | ||||
|  | ||||
|     /* TODO: should surfaces be freed after they cannot be referenced in atlas builing? */ | ||||
|     /*       for example, if full page of 64x64 tiles was already filled, there's no real reason to process them further */ | ||||
|     SDL_Surface *atlas_surface = cache->atlas_surfaces[cache->atlas_index]; | ||||
|     SDL_Surface *atlas_surface = cache->atlas_surfaces[atlas_index]; | ||||
|  | ||||
|     /* blit the texture surfaces onto the atlas */ | ||||
|     for (size_t i = 0; i < shlenu(cache->hash); ++i) { | ||||
|         /* skip all that aren't part of currently built one */ | ||||
|         if (cache->hash[i].value.atlas_index != cache->atlas_index) | ||||
|         if (cache->hash[i].value.atlas_index != atlas_index) | ||||
|             continue; | ||||
|  | ||||
|         /* skip loners */ | ||||
| @@ -284,7 +284,7 @@ static void recreate_current_atlas_texture(TextureCache *cache) { | ||||
|     } | ||||
|  | ||||
|     /* texturize it! */ | ||||
|     upload_texture_from_surface(cache->atlas_textures[cache->atlas_index], atlas_surface); | ||||
|     upload_texture_from_surface(cache->atlas_textures[atlas_index], atlas_surface); | ||||
|  | ||||
|     profile_end("atlas recreation"); | ||||
| } | ||||
| @@ -337,16 +337,18 @@ static stbrp_rect *filter_unpacked_rects(stbrp_rect *rects) { | ||||
|  | ||||
| /* updates the original rects array with the data from packed_rects */ | ||||
| /* returns true if all rects were packed successfully */ | ||||
| static bool update_rects(TextureCache *cache, stbrp_rect *rects, stbrp_rect *packed_rects) { | ||||
| static bool update_rects(TextureCache *cache, stbrp_rect *rects, stbrp_rect *packed_rects, int atlas_index) { | ||||
|     /* !!! do not grow either of the arrays !!! */ | ||||
|     /* the reallocation will try to reassign the array pointer, to no effect. */ | ||||
|     /* see stb_ds.h */ | ||||
|     bool packed_all = true; | ||||
|      | ||||
|  | ||||
|     for (size_t i = 0; i < arrlenu(packed_rects); ++i) { | ||||
|         /* we can check if any rects failed to be packed right here */ | ||||
|         /* it's not ideal, but it avoids another iteration */ | ||||
|         if (!packed_rects[i].was_packed) { | ||||
|             /* mark it as potentially filled in next atlas page */ | ||||
|             cache->hash[packed_rects[i].id].value.atlas_index = atlas_index + 1; | ||||
|             packed_all = false; | ||||
|             continue; | ||||
|         } | ||||
| @@ -355,7 +357,7 @@ static bool update_rects(TextureCache *cache, stbrp_rect *rects, stbrp_rect *pac | ||||
|         /* while the order of the elements in the hash map is unknown to us, */ | ||||
|         /* their equivalents in `rects` are in that same (unknown) order, which means */ | ||||
|         /* we can use the index we had saved to find the original texture struct */ | ||||
|         cache->hash[packed_rects[i].id].value.atlas_index = cache->atlas_index; | ||||
|         cache->hash[packed_rects[i].id].value.atlas_index = atlas_index; | ||||
|     } | ||||
|  | ||||
|     return packed_all; | ||||
| @@ -566,9 +568,6 @@ void textures_update_atlas(TextureCache *cache) { | ||||
|             upload_texture_from_surface(response.loner_texture, response.data); | ||||
|             response.srcrect = (Rect) { .w = (float)response.data->w, .h = (float)response.data->h }; | ||||
|  | ||||
|         } else { | ||||
|             /* will be fully populated as the atlas updates */ | ||||
|             response.atlas_index = cache->atlas_index; | ||||
|         } | ||||
|  | ||||
|         cache->hash[texture_load_queue[i].index].value = response; | ||||
| @@ -576,40 +575,42 @@ void textures_update_atlas(TextureCache *cache) { | ||||
|  | ||||
|     arrsetlen(texture_load_queue, 0); | ||||
|  | ||||
|     /* this function makes a lot more sense if you read stb_rect_pack.h */ | ||||
|     stbrp_context pack_ctx; /* target info */ | ||||
|     stbrp_init_target(&pack_ctx, | ||||
|                       (int)ctx.texture_atlas_size, | ||||
|                       (int)ctx.texture_atlas_size, | ||||
|                       cache->node_buffer, | ||||
|                       (int)ctx.texture_atlas_size); | ||||
|  | ||||
|     stbrp_rect *rects = create_rects_from_cache(cache); | ||||
|     int current_atlas_index = 0; | ||||
|  | ||||
|     /* we have to keep packing, and creating atlases if necessary, */ | ||||
|     /* until all rects have been packed. */ | ||||
|     /* ideally, this will not iterate more than once. */ | ||||
|     bool textures_remaining = true; | ||||
|     while (textures_remaining) { | ||||
|         /* this function makes a lot more sense if you read stb_rect_pack.h */ | ||||
|         stbrp_context pack_ctx; /* target info */ | ||||
|         stbrp_init_target(&pack_ctx, | ||||
|                           (int)ctx.texture_atlas_size, | ||||
|                           (int)ctx.texture_atlas_size, | ||||
|                           cache->node_buffer, | ||||
|                           (int)ctx.texture_atlas_size); | ||||
|  | ||||
|         stbrp_rect *rects_to_pack = filter_unpacked_rects(rects); | ||||
|         stbrp_pack_rects(&pack_ctx, rects_to_pack, (int)arrlen(rects_to_pack)); | ||||
|  | ||||
|         textures_remaining = !update_rects(cache, rects, rects_to_pack); | ||||
|         textures_remaining = !update_rects(cache, rects, rects_to_pack, current_atlas_index); | ||||
|         arrfree(rects_to_pack); /* got what we needed */ | ||||
|  | ||||
|         /* some textures couldn't be packed */ | ||||
|         if (textures_remaining) { | ||||
|             update_texture_rects_in_atlas(cache, rects); | ||||
|             recreate_current_atlas_texture(cache); | ||||
|             recreate_current_atlas_texture(cache, current_atlas_index); | ||||
|  | ||||
|             /* need a new atlas for next time */ | ||||
|             add_new_atlas(cache); | ||||
|             ++cache->atlas_index; | ||||
|             if (current_atlas_index >= cache->atlas_index) ++cache->atlas_index; | ||||
|             ++current_atlas_index; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     update_texture_rects_in_atlas(cache, rects); | ||||
|     recreate_current_atlas_texture(cache); | ||||
|     recreate_current_atlas_texture(cache, current_atlas_index); | ||||
|  | ||||
|     cache->is_dirty = false; | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user