From cc18611de8f69a821ed58b497a1f023d35b24376 Mon Sep 17 00:00:00 2001 From: Nico de Poel Date: Tue, 31 Jan 2023 12:21:17 +0100 Subject: [PATCH] Reorganized texture code so that the palette is loaded separately, and metadata used to calculate UVs are kept out of the exported file for PS1. --- main.cpp | 19 +++++++++++++------ ps1bsp.h | 2 -- texture.cpp | 40 ++++++++++++++++++++++++++++------------ texture.h | 26 +++++++++++++++++++++++++- 4 files changed, 66 insertions(+), 21 deletions(-) diff --git a/main.cpp b/main.cpp index 4d08089..7964651 100644 --- a/main.cpp +++ b/main.cpp @@ -46,7 +46,7 @@ static float computeFaceArea(const world_t* world, const face_t* face) typedef std::unordered_map> FacePolygons; -int process_faces(const world_t* world, const std::vector& textures) +int process_faces(const world_t* world, const TextureList& textures) { // Write some data to a file FILE* fbsp; @@ -134,7 +134,7 @@ int process_faces(const world_t* world, const std::vector& tex ps1bsp_face_t* outFace = &outFaces[faceIdx]; const texinfo_t* texinfo = &world->texInfos[face->texinfo_id]; const miptex_t* miptex = &world->miptexes[texinfo->texture_id]; - const ps1bsp_texture_t& ps1tex = textures[texinfo->texture_id]; + const auto& texture = textures[texinfo->texture_id]; outFace->firstPolygon = (unsigned short)outPolygons.size(); @@ -176,8 +176,8 @@ int process_faces(const world_t* world, const std::vector& tex ps1bsp_polyvertex_t polyVert = { 0 }; polyVert.index = (unsigned short)vertIndex; - polyVert.u = (unsigned char)(normalizedUV.x * (ps1tex.w - 1)) + ps1tex.uoffs; - polyVert.v = (unsigned char)(normalizedUV.y * (ps1tex.h - 1)) + ps1tex.voffs; + polyVert.u = (unsigned char)(normalizedUV.x * (texture.w - 1)) + texture.uoffs; + polyVert.v = (unsigned char)(normalizedUV.y * (texture.h - 1)) + texture.voffs; Vec3 vertex = tesselator.getVertices()[vertIndex]; int light = compute_faceVertex_light5(world, face, faceBounds, vertex); @@ -225,6 +225,13 @@ int process_faces(const world_t* world, const std::vector& tex outVertices.push_back(outVertex); } + // Copy PS1 texture data + std::vector outTextures; + for (auto texIter = textures.begin(); texIter != textures.end(); ++texIter) + { + outTextures.push_back(texIter->ps1tex); + } + // Convert planes std::vector outPlanes; for (int planeIdx = 0; planeIdx < world->numPlanes; ++planeIdx) @@ -277,7 +284,7 @@ int process_faces(const world_t* world, const std::vector& tex std::vector outVisData(world->visList, world->visList + world->visListLength); // Write collected data to file and update header info - writeMapData(textures, outHeader.textures, fbsp); + writeMapData(outTextures, outHeader.textures, fbsp); writeMapData(outVertices, outHeader.vertices, fbsp); writeMapData(outPolygons, outHeader.polygons, fbsp); writeMapData(outPolyVertices, outHeader.polyVertices, fbsp); @@ -304,7 +311,7 @@ int process_faces(const world_t* world, const std::vector& tex int process_bsp(const world_t *world) { // Test exporting texture data - std::vector textures; + TextureList textures; if (!process_textures(world, textures)) { return 0; diff --git a/ps1bsp.h b/ps1bsp.h index 0cf7197..f72cdeb 100644 --- a/ps1bsp.h +++ b/ps1bsp.h @@ -43,8 +43,6 @@ typedef struct typedef struct { - unsigned char w, h; // These may be necessary for scaling UVs, especially since we use a mix of mip0 and mip1 textures - unsigned char uoffs, voffs; // Texture coordinate offset within the texture page unsigned short tpage; // Texture page in PS1 VRAM (precalculated when generating the texture atlas) unsigned short nextframe; // If non-zero, the texture is animated and this points to the next texture in the sequence } ps1bsp_texture_t; diff --git a/texture.cpp b/texture.cpp index e20a4db..61d98bf 100644 --- a/texture.cpp +++ b/texture.cpp @@ -36,7 +36,7 @@ static void desaturate(const unsigned char inColor[3], unsigned char outColor[3] outColor[2] = (unsigned char)((double)inColor[2] + f * (L - inColor[2])); } -static bool generate_clut(const char* paletteFile, tim::PARAM* outTim) +static bool load_palette(const char* paletteFile, Color outPalette[PALETTE_SIZE]) { unsigned char palette[PALETTE_SIZE * 3]; @@ -48,6 +48,19 @@ static bool generate_clut(const char* paletteFile, tim::PARAM* outTim) fread(palette, sizeof(unsigned char) * 3, PALETTE_SIZE, fp); fclose(fp); + for (int c = 0; c < PALETTE_SIZE; ++c) + { + outPalette[c].rgb.r = palette[3 * c + 0]; + outPalette[c].rgb.g = palette[3 * c + 1]; + outPalette[c].rgb.b = palette[3 * c + 2]; + outPalette[c].rgb.a = 0; + } + + return true; +} + +static bool generate_clut(const Color palette[PALETTE_SIZE], tim::PARAM* outTim) +{ tim::PIX_RGB5* clut = (tim::PIX_RGB5*)malloc(PALETTE_SIZE * sizeof(tim::PIX_RGB5) * 2); if (clut == NULL) return false; @@ -57,7 +70,7 @@ static bool generate_clut(const char* paletteFile, tim::PARAM* outTim) for (int c = 0; c < PALETTE_SIZE - 1; ++c) // Final palette entry is for alpha masking { unsigned char color[3]; - desaturate(&palette[3 * c], color); + desaturate(&palette[c].rgb.r, color); clut[c].r = color[0] >> 3; clut[c].g = color[1] >> 3; @@ -82,7 +95,7 @@ static bool generate_clut(const char* paletteFile, tim::PARAM* outTim) return true; } -bool process_textures(const world_t* world, std::vector& outTextures) // TODO: return TextureDescriptor structs, including average texture color +bool process_textures(const world_t* world, TextureList& outTextures) // TODO: return TextureDescriptor structs, including average texture color { using spaces_type = rectpack2D::empty_spaces; using rect_type = rectpack2D::output_rect_t; @@ -146,13 +159,16 @@ 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); + Color palette[PALETTE_SIZE]; + load_palette("palette.lmp", palette); + tim::PARAM outTim = { 0 }; outTim.format = 1; // 8-bit per pixel, all Quake textures use this outTim.imgXoffs = 512; outTim.imgYoffs = 256; outTim.clutXoffs = 512; outTim.clutYoffs = 0; - generate_clut("palette.lmp", &outTim); + generate_clut(palette, &outTim); outTim.imgWidth = result_size.w; outTim.imgHeight = result_size.h; @@ -168,7 +184,7 @@ bool process_textures(const world_t* world, std::vector& outTe miptex_t* miptex = &world->miptexes[texNum]; if (miptex->name[0] == '\0') // Weird edge case on N64START.bsp, corrupt data perhaps? { - outTextures.push_back(ps1bsp_texture_t{ 0 }); // We have to add something, otherwise the texture IDs get messed up + outTextures.push_back(TextureDescriptor{ 0 }); // We have to add something, otherwise the texture IDs get messed up continue; } @@ -189,19 +205,19 @@ bool process_textures(const world_t* world, std::vector& outTe 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 }; - ps1tex.w = (u_char)rectangle.w; - ps1tex.h = (u_char)rectangle.h; + TextureDescriptor tex = { 0 }; + tex.w = (u_char)rectangle.w; + tex.h = (u_char)rectangle.h; u_short x = (rectangle.x / 2) + outTim.imgXoffs; // Divide by 2 to get the coordinate in 16-bit pixel units u_short y = rectangle.y + outTim.imgYoffs; - ps1tex.tpage = getTPage(outTim.format, 0, x, y); - ps1tex.uoffs = (u_char)((x % 64) << (2 - outTim.format)); - ps1tex.voffs = (u_char)(y & 0xFF); + tex.ps1tex.tpage = getTPage(outTim.format, 0, x, y); + tex.uoffs = (u_char)((x % 64) << (2 - outTim.format)); + tex.voffs = (u_char)(y & 0xFF); // TODO: animated textures - outTextures.push_back(ps1tex); + outTextures.push_back(tex); } } } diff --git a/texture.h b/texture.h index 6356d8c..59bf337 100644 --- a/texture.h +++ b/texture.h @@ -1,3 +1,27 @@ #pragma once -bool process_textures(const world_t* world, std::vector& outTextures); +struct Color +{ + union + { + struct + { + unsigned char a, r, g, b; + } rgb; + unsigned int value; + unsigned char channel[4]; + }; +}; + +struct TextureDescriptor +{ + ps1bsp_texture_t ps1tex; + unsigned char w, h; // Width and height of the selected texture mip level + unsigned char uoffs, voffs; // Texture coordinate offset within the texture page + Color averageColor; + Color medianColor; +}; + +typedef std::vector TextureList; + +bool process_textures(const world_t* world, TextureList& outTextures);