Browse Source

Modified texture packer to allow for arbitrary input bin sizes. Added transparency flag to final palette entry.

master
Nico de Poel 3 years ago
parent
commit
5e85e2c73a
  1. 2
      rectpack/best_bin_finder.h
  2. 6
      rectpack/finders_interface.h
  3. 30
      texture.cpp

2
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;

6
rectpack/finders_interface.h

@ -17,7 +17,7 @@ namespace rectpack2D {
template <class F, class G>
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 <class F, class G>
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<F, G> {
max_bin_side,
max_bin,
discard_step,
std::forward<F>(handle_successful_insertion),
std::forward<G>(handle_unsuccessful_insertion),

30
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<ps1bsp_texture_t>& 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<rect_type> rectangles;
@ -99,11 +100,10 @@ bool process_textures(const world_t* world, std::vector<ps1bsp_texture_t>& 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<spaces_type>(
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<ps1bsp_texture_t>& 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<ps1bsp_texture_t>& 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<ps1bsp_texture_t>& 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<ps1bsp_texture_t>& 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);

Loading…
Cancel
Save