#include #include #include #include "bsp.h" #include "rectpack/finders_interface.h" int main(int argc, char** argv) { FILE* f; dheader_t header; char path[_MAX_PATH]; fopen_s(&f, argv[1], "rb"); if (f == NULL) return 1; fread(&header, sizeof(dheader_t), 1, f); printf("Header model version: %d\n", header.version); // Test reading the entity string data fseek(f, header.entities.offset, SEEK_SET); char* entities = (char*)malloc((header.entities.size + 1) * sizeof(char)); if (entities == NULL) { fclose(f); return 1; } memset(entities, 0, (header.entities.size + 1) * sizeof(char)); fread(entities, sizeof(char), header.entities.size, f); printf("Entities list:\n%s\n", entities); free(entities); // Test exporting texture data fseek(f, header.miptex.offset, SEEK_SET); mipheader_t mipheader; fread(&mipheader.numtex, sizeof(long), 1, f); mipheader.offset = (long*)malloc(mipheader.numtex * sizeof(long)); if (mipheader.offset == NULL) { fclose(f); return 1; } fread(mipheader.offset, sizeof(long), mipheader.numtex, f); using spaces_type = rectpack2D::empty_spaces; using rect_type = rectpack2D::output_rect_t; auto report_successful = [](rect_type&) { return rectpack2D::callback_result::CONTINUE_PACKING; }; auto report_unsuccessful = [](rect_type&) { 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 256x512 in practice, or a quarter of the PS1's VRAM allocation. const auto discard_step = -4; std::vector rectangles; miptex_t miptex; // Try some texture packing and see if we fit inside the PS1's VRAM for (int texNum = 0; texNum < mipheader.numtex; ++texNum) { unsigned long miptexOffset = header.miptex.offset + mipheader.offset[texNum]; fseek(f, miptexOffset, SEEK_SET); fread(&miptex, sizeof(miptex_t), 1, f); //printf("Texture %d (%dx%d): %.16s\n", texNum, miptex.width, miptex.height, miptex.name); // Shrink the larger textures, but keep smaller ones at their original size int ps1mip = miptex.width > 64 || miptex.height > 64 ? 1 : 0; if (strcmp(miptex.name, "clip") && strcmp(miptex.name, "trigger")) rectangles.emplace_back(rectpack2D::rect_xywh(0, 0, miptex.width >> ps1mip, miptex.height >> ps1mip)); else rectangles.emplace_back(rectpack2D::rect_xywh(0, 0, 0, 0)); } // 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, discard_step, report_successful, report_unsuccessful, rectpack2D::flipping_option::DISABLED ) ); printf("%d textures. Packed texture atlas size: %d x %d\n", mipheader.numtex, result_size.w, result_size.h); unsigned char* atlas = (unsigned char*)malloc(result_size.w * result_size.h * sizeof(unsigned char)); memset(atlas, 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 < mipheader.numtex; ++texNum) { unsigned long miptexOffset = header.miptex.offset + mipheader.offset[texNum]; fseek(f, miptexOffset, SEEK_SET); fread(&miptex, sizeof(miptex_t), 1, f); char* outName = miptex.name; if (*outName == '*' || *outName == '+') outName++; for (int mipLevel = 0; mipLevel < 4; ++mipLevel) { unsigned long mipOffset = *(&miptex.offset1 + mipLevel); fseek(f, miptexOffset + mipOffset, SEEK_SET); size_t numBytes = (miptex.width * miptex.height) >> mipLevel; unsigned char* texBytes = (unsigned char*)malloc(sizeof(unsigned char) * numBytes); fread(texBytes, sizeof(unsigned char), numBytes, f); FILE* fout; sprintf_s(path, _MAX_PATH, "textures/%s-mip%d-%dx%d.raw", outName, mipLevel, miptex.width >> mipLevel, miptex.height >> mipLevel); fopen_s(&fout, path, "wb"); fwrite(texBytes, sizeof(unsigned char), numBytes, fout); fclose(fout); 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 { //printf("Writing texture %s mip %d to position: (%d, %d) w = %d, h = %d\n", miptex.name, mipLevel, rectangle.x, rectangle.y, rectangle.w, rectangle.h); 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)); } } free(texBytes); } } FILE* fatlas; sprintf_s(path, _MAX_PATH, "atlas-%dx%d.raw", result_size.w, result_size.h); fopen_s(&fatlas, path, "wb"); fwrite(atlas, sizeof(unsigned char), result_size.w * result_size.h, fatlas); fclose(fatlas); free(atlas); free(mipheader.offset); // Inspect vertex data int numVertices = header.vertices.size / sizeof(vertex_t); int numFaces = header.faces.size / sizeof(face_t); fseek(f, header.vertices.offset, SEEK_SET); vertex_t* vertices = (vertex_t*)malloc(header.vertices.size); fread(vertices, sizeof(vertex_t), numVertices, f); vec3_t min = { FLT_MAX, FLT_MAX, FLT_MAX }, max = { -FLT_MAX, -FLT_MAX, -FLT_MAX }; for (int vertIdx = 0; vertIdx < numVertices; ++vertIdx) { vertex_t* vert = &vertices[vertIdx]; if (vert->X > max.x) max.x = vert->X; if (vert->Y > max.y) max.y = vert->Y; if (vert->Z > max.z) max.z = vert->Z; if (vert->X < min.x) min.x = vert->X; if (vert->Y < min.y) min.y = vert->Y; if (vert->Z < min.z) min.z = vert->Z; } printf("%d vertices, %d faces, min = (%f, %f, %f), max = (%f, %f, %f)\n", numVertices, numFaces, min.x, min.y, min.z, max.x, max.y, max.z); const int fixedScale = 1 << 14; int fixedMin[3] = { (int)(min.x * fixedScale), (int)(min.y * fixedScale), (int)(min.z * fixedScale) }; int fixedMax[3] = { (int)(max.x * fixedScale), (int)(max.y * fixedScale), (int)(max.z * fixedScale) }; printf("Fixed point min = (%d, %d, %d), max = (%d, %d, %d)\n", fixedMin[0], fixedMin[1], fixedMin[2], fixedMax[0], fixedMax[1], fixedMax[2]); fclose(f); return 0; }