From eb3de1b6244bab588c1506a57693e1a7db8efded Mon Sep 17 00:00:00 2001 From: Nico de Poel Date: Tue, 14 Feb 2023 13:18:49 +0100 Subject: [PATCH] Pack repeatable textures first, followed by other textures in the remaining space. This is actually producing more efficient textures atlases. The hell. --- texture.cpp | 42 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 36 insertions(+), 6 deletions(-) diff --git a/texture.cpp b/texture.cpp index e6ab4ef..d0fe71b 100644 --- a/texture.cpp +++ b/texture.cpp @@ -253,12 +253,11 @@ bool process_textures(const world_t* world, TextureList& outTextures) outTim.clutYoffs = 0; generate_clut(palette, &outTim); - const auto max_bin = rectpack2D::rect_wh(1024, 256); // 8-bit textures take up half the horizontal space so this is 512x256 in practice, or a quarter of the PS1's VRAM allocation. + auto max_bin = rectpack2D::rect_wh(1024, 256); // 8-bit textures take up half the horizontal space so this is 512x256 in practice, or a quarter of the PS1's VRAM allocation. printf("Finding best texture packing...\n"); - std::map textures; - std::vector rectangles; + std::map textures, repeatables; // Try some texture packing and see if we fit inside the PS1's VRAM for (int texNum = 0; texNum < world->mipheader.numtex; ++texNum) @@ -269,10 +268,41 @@ bool process_textures(const world_t* world, TextureList& outTextures) //printf("Texture %d (%dx%d): %.16s\n", texNum, miptex->width, miptex->height, miptex->name); - textures[texNum] = miptex; + if (texture_isRepeatable(miptex)) + repeatables[texNum] = miptex; + else + textures[texNum] = miptex; } - const auto result_size = pack_textures(textures, max_bin, rectangles); + // Pack the repeatable textures first, so that they get placed at predictable locations + // rectpack2D is set up to pack larger textures first, so this actually works out with power-of-two textures + std::vector rRects; + const auto rSize = pack_textures(repeatables, max_bin, rRects); + + max_bin.w -= rSize.w; + + // Pack the remaining textures into the remaining space + std::vector tRects; + const auto tSize = pack_textures(textures, max_bin, tRects); + + auto result_size = rectpack2D::rect_wh(rSize.w + tSize.w, max(rSize.h, tSize.h)); + + // Collect all of the texture rectangles back into a single in-order list again + std::vector rectangles; + auto rIter = rRects.begin(); + auto tIter = tRects.begin(); + for (int texNum = 0; texNum < world->mipheader.numtex; ++texNum) + { + if (repeatables.find(texNum) != repeatables.end()) + { + rectangles.push_back(*rIter++); + continue; + } + + auto rect = *tIter++; + rect.x += rSize.w; + rectangles.push_back(rect); + } printf("%d textures. Packed texture atlas size: %d x %d\n", world->mipheader.numtex, result_size.w, result_size.h); @@ -320,7 +350,7 @@ bool process_textures(const world_t* world, TextureList& outTextures) tex.ps1tex.tpage = getTPage(outTim.format, 0, x, y); tex.uoffs = (u_char)((x % 64) << (2 - outTim.format)); tex.voffs = (u_char)(y & 0xFF); - tex.ps1tex.twin = getTexWindow(tex.uoffs, tex.voffs, tex.w, tex.h); // TODO: figure out the right offsets that are multiples of w and h + tex.ps1tex.twin = getTexWindow(tex.uoffs, tex.voffs, tex.w, tex.h); // TODO: figure out the right offsets that are multiples of w and h; NOTE: if uoffs has to >> 1, then w probably has to as well } }