From 5e85e2c73a9b420c380087e7d5d8c876797b322a Mon Sep 17 00:00:00 2001 From: Nico de Poel Date: Tue, 24 Jan 2023 12:59:16 +0100 Subject: [PATCH] Modified texture packer to allow for arbitrary input bin sizes. Added transparency flag to final palette entry. --- rectpack/best_bin_finder.h | 2 +- rectpack/finders_interface.h | 6 +++--- texture.cpp | 30 +++++++++++++++--------------- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/rectpack/best_bin_finder.h b/rectpack/best_bin_finder.h index f33ef97..474679d 100644 --- a/rectpack/best_bin_finder.h +++ b/rectpack/best_bin_finder.h @@ -214,7 +214,7 @@ namespace rectpack2D { class I > rect_wh find_best_packing_impl(F for_each_order, const I input) { - const auto max_bin = rect_wh(input.max_bin_side << 1, input.max_bin_side >> 1); // PS1BSP: only way I've been able to make non-square output work ¯\_(ツ)_/¯ + const auto max_bin = input.max_bin; OrderType* best_order = nullptr; diff --git a/rectpack/finders_interface.h b/rectpack/finders_interface.h index c3ef821..48af3f3 100644 --- a/rectpack/finders_interface.h +++ b/rectpack/finders_interface.h @@ -17,7 +17,7 @@ namespace rectpack2D { template struct finder_input { - const int max_bin_side; + const rect_wh max_bin; const int discard_step; F handle_successful_insertion; G handle_unsuccessful_insertion; @@ -26,14 +26,14 @@ namespace rectpack2D { template auto make_finder_input( - const int max_bin_side, + const rect_wh max_bin, const int discard_step, F&& handle_successful_insertion, G&& handle_unsuccessful_insertion, const flipping_option flipping_mode ) { return finder_input { - max_bin_side, + max_bin, discard_step, std::forward(handle_successful_insertion), std::forward(handle_unsuccessful_insertion), diff --git a/texture.cpp b/texture.cpp index 2c5a661..75a7b32 100644 --- a/texture.cpp +++ b/texture.cpp @@ -46,7 +46,7 @@ static bool generate_clut(const char* paletteFile, tim::PARAM* outTim) clut[c].r = palette[3 * c + 0] >> 3; clut[c].g = palette[3 * c + 1] >> 3; clut[c].b = palette[3 * c + 2] >> 3; - clut[c].i = 0; + clut[c].i = (c == 255); // Final palette entry is for transparencies // Completely black pixels are regarded as transparent by the PS1, so prevent that from happening by making those palette entries *nearly* black if (clut[c].r == 0 && clut[c].g == 0 && clut[c].b == 0) @@ -69,10 +69,11 @@ bool process_textures(const world_t* world, std::vector& outTe }; auto report_unsuccessful = [](rect_type&) { + printf("Failed to fit all textures into atlas!\n"); return rectpack2D::callback_result::ABORT_PACKING; }; - const auto max_side = 512; // Max height of PS1 VRAM. 8-bit textures take up half the horizontal space so this is 512x256 in practice, or a quarter of the PS1's VRAM allocation. + 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. const auto discard_step = -4; std::vector rectangles; @@ -99,11 +100,10 @@ bool process_textures(const world_t* world, std::vector& outTe rectangles.emplace_back(rectpack2D::rect_xywh(0, 0, miptex->width >> 3, miptex->width >> 3)); // Add the lowest mip level so that it at least gets included in the final texture list, and we don't mess up the texture IDs } - // Automatic atlas packing. Nice but it tries to make a square atlas which is not what we want. (This is solved by hacking the header itself) const auto result_size = rectpack2D::find_best_packing( rectangles, rectpack2D::make_finder_input( - max_side, + max_bin, discard_step, report_successful, report_unsuccessful, @@ -112,12 +112,7 @@ bool process_textures(const world_t* world, std::vector& outTe ); printf("%d textures. Packed texture atlas size: %d x %d\n", world->mipheader.numtex, result_size.w, result_size.h); - unsigned char* atlas = (unsigned char*)malloc(result_size.w * result_size.h * sizeof(unsigned char)); - if (atlas == NULL) - return false; - - memset(atlas, 0, result_size.w * result_size.h * sizeof(unsigned char)); - + tim::PARAM outTim = { 0 }; outTim.format = 1; // 8-bit per pixel, all Quake textures use this outTim.imgXoffs = 512; @@ -126,6 +121,14 @@ bool process_textures(const world_t* world, std::vector& outTe outTim.clutYoffs = 0; generate_clut("palette.lmp", &outTim); + outTim.imgWidth = result_size.w; + outTim.imgHeight = result_size.h; + outTim.imgData = malloc(result_size.w * result_size.h * sizeof(unsigned char)); + if (outTim.imgData == NULL) + return false; + + memset(outTim.imgData, 0, result_size.w * result_size.h * sizeof(unsigned char)); + // Try to construct the texture atlas, see what we get for (int texNum = 0; texNum < world->mipheader.numtex; ++texNum) { @@ -147,9 +150,10 @@ bool process_textures(const world_t* world, std::vector& outTe const auto& rectangle = rectangles[texNum]; if (miptex->width >> mipLevel == rectangle.w) // This is the mip level we've previously decided we want for our PS1 atlas { + // Copy the source texture line by line into the atlas at the desired position for (int y = 0; y < rectangle.h; ++y) { - memcpy_s(atlas + ((rectangle.y + y) * result_size.w + rectangle.x), rectangle.w * sizeof(unsigned char), texBytes + (y * rectangle.w), rectangle.w * sizeof(unsigned char)); + memcpy_s((unsigned char*)outTim.imgData + ((rectangle.y + y) * result_size.w + rectangle.x), rectangle.w * sizeof(unsigned char), texBytes + (y * rectangle.w), rectangle.w * sizeof(unsigned char)); } ps1bsp_texture_t ps1tex = { 0 }; @@ -169,10 +173,6 @@ bool process_textures(const world_t* world, std::vector& outTe } } - outTim.imgData = atlas; - outTim.imgWidth = result_size.w; - outTim.imgHeight = result_size.h; - sprintf_s(path, _MAX_PATH, "atlas-%s.tim", world->name); tim::ExportFile(path, &outTim);