Browse Source

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.

master
Nico de Poel 3 years ago
parent
commit
cc18611de8
  1. 19
      main.cpp
  2. 2
      ps1bsp.h
  3. 40
      texture.cpp
  4. 26
      texture.h

19
main.cpp

@ -46,7 +46,7 @@ static float computeFaceArea(const world_t* world, const face_t* face)
typedef std::unordered_map<const face_t*, std::vector<Tesselator::Polygon>> FacePolygons;
int process_faces(const world_t* world, const std::vector<ps1bsp_texture_t>& 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<ps1bsp_texture_t>& 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<ps1bsp_texture_t>& 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<ps1bsp_texture_t>& tex
outVertices.push_back(outVertex);
}
// Copy PS1 texture data
std::vector<ps1bsp_texture_t> outTextures;
for (auto texIter = textures.begin(); texIter != textures.end(); ++texIter)
{
outTextures.push_back(texIter->ps1tex);
}
// Convert planes
std::vector<ps1bsp_plane_t> outPlanes;
for (int planeIdx = 0; planeIdx < world->numPlanes; ++planeIdx)
@ -277,7 +284,7 @@ int process_faces(const world_t* world, const std::vector<ps1bsp_texture_t>& tex
std::vector<unsigned char> 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<ps1bsp_texture_t>& tex
int process_bsp(const world_t *world)
{
// Test exporting texture data
std::vector<ps1bsp_texture_t> textures;
TextureList textures;
if (!process_textures(world, textures))
{
return 0;

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

40
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<ps1bsp_texture_t>& 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<false>;
using rect_type = rectpack2D::output_rect_t<spaces_type>;
@ -146,13 +159,16 @@ 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);
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<ps1bsp_texture_t>& 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<ps1bsp_texture_t>& 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);
}
}
}

26
texture.h

@ -1,3 +1,27 @@
#pragma once
bool process_textures(const world_t* world, std::vector<ps1bsp_texture_t>& 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<TextureDescriptor> TextureList;
bool process_textures(const world_t* world, TextureList& outTextures);
Loading…
Cancel
Save