twn_textures.c: fix atlas packing, allow out of order population
This commit is contained in:
parent
ed8e826b94
commit
a97515e948
@ -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");
|
profile_start("atlas recreation");
|
||||||
|
|
||||||
/* TODO: should surfaces be freed after they cannot be referenced in atlas builing? */
|
/* 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 */
|
/* 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 */
|
/* blit the texture surfaces onto the atlas */
|
||||||
for (size_t i = 0; i < shlenu(cache->hash); ++i) {
|
for (size_t i = 0; i < shlenu(cache->hash); ++i) {
|
||||||
/* skip all that aren't part of currently built one */
|
/* 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;
|
continue;
|
||||||
|
|
||||||
/* skip loners */
|
/* skip loners */
|
||||||
@ -284,7 +284,7 @@ static void recreate_current_atlas_texture(TextureCache *cache) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* texturize it! */
|
/* 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");
|
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 */
|
/* updates the original rects array with the data from packed_rects */
|
||||||
/* returns true if all rects were packed successfully */
|
/* 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 !!! */
|
/* !!! do not grow either of the arrays !!! */
|
||||||
/* the reallocation will try to reassign the array pointer, to no effect. */
|
/* the reallocation will try to reassign the array pointer, to no effect. */
|
||||||
/* see stb_ds.h */
|
/* see stb_ds.h */
|
||||||
bool packed_all = true;
|
bool packed_all = true;
|
||||||
|
|
||||||
for (size_t i = 0; i < arrlenu(packed_rects); ++i) {
|
for (size_t i = 0; i < arrlenu(packed_rects); ++i) {
|
||||||
/* we can check if any rects failed to be packed right here */
|
/* we can check if any rects failed to be packed right here */
|
||||||
/* it's not ideal, but it avoids another iteration */
|
/* it's not ideal, but it avoids another iteration */
|
||||||
if (!packed_rects[i].was_packed) {
|
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;
|
packed_all = false;
|
||||||
continue;
|
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, */
|
/* 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 */
|
/* 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 */
|
/* 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;
|
return packed_all;
|
||||||
@ -566,9 +568,6 @@ void textures_update_atlas(TextureCache *cache) {
|
|||||||
upload_texture_from_surface(response.loner_texture, response.data);
|
upload_texture_from_surface(response.loner_texture, response.data);
|
||||||
response.srcrect = (Rect) { .w = (float)response.data->w, .h = (float)response.data->h };
|
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;
|
cache->hash[texture_load_queue[i].index].value = response;
|
||||||
@ -576,40 +575,42 @@ void textures_update_atlas(TextureCache *cache) {
|
|||||||
|
|
||||||
arrsetlen(texture_load_queue, 0);
|
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);
|
stbrp_rect *rects = create_rects_from_cache(cache);
|
||||||
|
int current_atlas_index = 0;
|
||||||
|
|
||||||
/* we have to keep packing, and creating atlases if necessary, */
|
/* we have to keep packing, and creating atlases if necessary, */
|
||||||
/* until all rects have been packed. */
|
/* until all rects have been packed. */
|
||||||
/* ideally, this will not iterate more than once. */
|
/* ideally, this will not iterate more than once. */
|
||||||
bool textures_remaining = true;
|
bool textures_remaining = true;
|
||||||
while (textures_remaining) {
|
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_rect *rects_to_pack = filter_unpacked_rects(rects);
|
||||||
stbrp_pack_rects(&pack_ctx, rects_to_pack, (int)arrlen(rects_to_pack));
|
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 */
|
arrfree(rects_to_pack); /* got what we needed */
|
||||||
|
|
||||||
/* some textures couldn't be packed */
|
/* some textures couldn't be packed */
|
||||||
if (textures_remaining) {
|
if (textures_remaining) {
|
||||||
update_texture_rects_in_atlas(cache, rects);
|
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 */
|
/* need a new atlas for next time */
|
||||||
add_new_atlas(cache);
|
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);
|
update_texture_rects_in_atlas(cache, rects);
|
||||||
recreate_current_atlas_texture(cache);
|
recreate_current_atlas_texture(cache, current_atlas_index);
|
||||||
|
|
||||||
cache->is_dirty = false;
|
cache->is_dirty = false;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user