Browse Source

Added a few more bits of BSP data, fixed edge list which is in fact an array of ints; the documentation was wrong.

master
Nico de Poel 3 years ago
parent
commit
2f3facc4d8
  1. 17
      bsp.h
  2. 109
      main.cpp
  3. 9
      ps1bsp.h

17
bsp.h

@ -44,6 +44,15 @@ typedef struct // Vector or Position
scalar_t z; // vertical scalar_t z; // vertical
} vec3_t; } vec3_t;
typedef struct
{
vec3_t normal; // Vector orthogonal to plane (Nx,Ny,Nz)
// with Nx2+Ny2+Nz2 = 1
scalar_t dist; // Offset to plane, along the normal vector.
// Distance from (0,0,0) to the plane
long type; // Type of plane, depending on normal vector.
} plane_t;
typedef struct // Bounding Box, Float values typedef struct // Bounding Box, Float values
{ {
vec3_t min; // minimum values of X,Y,Z vec3_t min; // minimum values of X,Y,Z
@ -142,6 +151,9 @@ typedef struct
miptex_t* miptexes; miptex_t* miptexes;
unsigned char** textures; unsigned char** textures;
int numPlanes;
plane_t* planes;
int numVertices; int numVertices;
vertex_t* vertices; vertex_t* vertices;
@ -149,7 +161,7 @@ typedef struct
edge_t* edges; edge_t* edges;
int edgeListLength; int edgeListLength;
unsigned short* edgeList;
int* edgeList;
int numFaces; int numFaces;
face_t* faces; face_t* faces;
@ -157,6 +169,9 @@ typedef struct
int faceListLength; int faceListLength;
unsigned short* faceList; unsigned short* faceList;
int visListLength;
unsigned char *visList;
int numNodes; int numNodes;
node_t* nodes; node_t* nodes;

109
main.cpp

@ -37,8 +37,10 @@ int process_textures(const world_t* world)
for (int texNum = 0; texNum < world->mipheader.numtex; ++texNum) for (int texNum = 0; texNum < world->mipheader.numtex; ++texNum)
{ {
miptex_t *miptex = &world->miptexes[texNum]; miptex_t *miptex = &world->miptexes[texNum];
if (miptex->name[0] == '\0') // Weird edge case on N64START.bsp, corrupt data perhaps?
miptex->width = miptex->height = 0;
//printf("Texture %d (%dx%d): %.16s\n", texNum, miptex.width, miptex.height, miptex.name);
//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 // Shrink the larger textures, but keep smaller ones at their original size
int ps1mip = miptex->width > 64 || miptex->height > 64 ? 1 : 0; int ps1mip = miptex->width > 64 || miptex->height > 64 ? 1 : 0;
@ -72,6 +74,8 @@ int process_textures(const world_t* world)
for (int texNum = 0; texNum < world->mipheader.numtex; ++texNum) for (int texNum = 0; texNum < world->mipheader.numtex; ++texNum)
{ {
miptex_t* miptex = &world->miptexes[texNum]; miptex_t* miptex = &world->miptexes[texNum];
if (miptex->name[0] == '\0') // Weird edge case on N64START.bsp, corrupt data perhaps?
continue;
char* outName = miptex->name; char* outName = miptex->name;
if (*outName == '*' || *outName == '+') if (*outName == '*' || *outName == '+')
@ -94,7 +98,7 @@ int process_textures(const world_t* world)
const auto& rectangle = rectangles[texNum]; 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 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);
//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) 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(atlas + ((rectangle.y + y) * result_size.w + rectangle.x), rectangle.w * sizeof(unsigned char), texBytes + (y * rectangle.w), rectangle.w * sizeof(unsigned char));
@ -146,6 +150,20 @@ typedef struct
unsigned short index; unsigned short index;
} vertexref_t; } vertexref_t;
// Determines a floating origin for the given leaf
static void leaf_zone(const dleaf_t* leaf, short zone[3])
{
const unsigned short mask = 0xFE00; // Zero out the 9 least significant bits
short midX = (leaf->bound.min[0] + leaf->bound.max[0]) / 2;
short midY = (leaf->bound.min[1] + leaf->bound.max[1]) / 2;
short midZ = (leaf->bound.min[2] + leaf->bound.max[2]) / 2;
zone[0] = midX & mask;
zone[1] = midY & mask;
zone[2] = midZ & mask;
}
int process_faces(const world_t* world) int process_faces(const world_t* world)
{ {
// Write some data to a file // Write some data to a file
@ -158,21 +176,21 @@ int process_faces(const world_t* world)
fwrite(&outHeader, sizeof(ps1bsp_header_t), 1, fbsp); fwrite(&outHeader, sizeof(ps1bsp_header_t), 1, fbsp);
// TODO: convert vertices and group them by material properties (texture, lightmap) and floating origin zone (based on leaf data), duplicate where necessary // TODO: convert vertices and group them by material properties (texture, lightmap) and floating origin zone (based on leaf data), duplicate where necessary
// Organize vertex indices so we can shuffle them around and duplicate them, while keeping track of which vertex is where
std::unordered_map<vertex_t*, vertexref_t> vertexRefs;
for (unsigned short i = 0; i < world->header.vertices.size / sizeof(vertex_t); ++i)
short zone[3];
for (int leafIdx = 0; leafIdx < world->numLeaves; ++leafIdx)
{ {
vertex_t* v = &world->vertices[i];
vertexRefs[v] = vertexref_t{ v, i };
dleaf_t* leaf = &world->leaves[leafIdx];
leaf_zone(leaf, zone);
//printf("Leaf %d zone (%d, %d, %d) %d face(s)\n", leafIdx, zone[0], zone[1], zone[2], leaf->lface_num);
} }
// Write vertex data to a file (no vertex splitting yet) // Write vertex data to a file (no vertex splitting yet)
for (unsigned short i = 0; i < world->header.vertices.size / sizeof(vertex_t); ++i)
for (unsigned short i = 0; i < world->numVertices; ++i)
{ {
// TODO: we should respect the ordering from vertexRef->index here but meh, problem for later // TODO: we should respect the ordering from vertexRef->index here but meh, problem for later
vertex_t* inVertex = &world->vertices[i]; vertex_t* inVertex = &world->vertices[i];
ps1bsp_vertex_t outVertex = { 0 }; ps1bsp_vertex_t outVertex = { 0 };
// Ensure we don't overflow 16-bit short values. Most Quake maps will stay within these bounds so it *should* be fine (for now).
if (inVertex->X > -8192 && inVertex->X < 8192 && inVertex->Y > -8192 && inVertex->Y < 8192 && inVertex->Z > -8192 && inVertex->Z < 8192) if (inVertex->X > -8192 && inVertex->X < 8192 && inVertex->Y > -8192 && inVertex->Y < 8192 && inVertex->Z > -8192 && inVertex->Z < 8192)
{ {
outVertex.x = (short)(inVertex->X * 4); outVertex.x = (short)(inVertex->X * 4);
@ -184,39 +202,66 @@ int process_faces(const world_t* world)
fwrite(&outVertex, sizeof(ps1bsp_vertex_t), 1, fbsp); fwrite(&outVertex, sizeof(ps1bsp_vertex_t), 1, fbsp);
} }
outHeader.numVertices = (unsigned short)(world->header.vertices.size / sizeof(vertex_t));
std::vector<ps1bsp_triangle_t> outTriangles;
std::vector<ps1bsp_face_t> outFaces;
std::vector<vertexref_t*> faceVerts;
std::vector<unsigned short> faceVertIndices;
for (int faceIdx = 0; faceIdx < world->numFaces; ++faceIdx) for (int faceIdx = 0; faceIdx < world->numFaces; ++faceIdx)
{ {
face_t* face = &world->faces[faceIdx]; face_t* face = &world->faces[faceIdx];
// Traverse the list of face edges to collect all of the face's vertices
for (int edgeListIdx = 0; edgeListIdx < face->ledge_num; ++edgeListIdx) for (int edgeListIdx = 0; edgeListIdx < face->ledge_num; ++edgeListIdx)
{ {
short edgeIdx = world->edgeList[face->ledge_id + edgeListIdx];
int edgeIdx = world->edgeList[face->ledge_id + edgeListIdx];
int vertIndex = edgeIdx > 0 ?
unsigned short vertIndex = edgeIdx > 0 ?
world->edges[edgeIdx].vertex0 : world->edges[edgeIdx].vertex0 :
world->edges[-edgeIdx].vertex1; world->edges[-edgeIdx].vertex1;
vertex_t* v = &world->vertices[vertIndex];
faceVerts.push_back(&vertexRefs[v]);
faceVertIndices.push_back(vertIndex);
} }
printf("Face %d: %d vertices\n", faceIdx, faceVerts.size());
ps1bsp_face_t outFace = { 0 };
outFace.firstTriangleId = outTriangles.size();
//printf("Face %d: %d vertices\n", faceIdx, faceVerts.size());
// Triangulate face into polygons (triangle fan, the naive method) // Triangulate face into polygons (triangle fan, the naive method)
// TODO better method: generate a quad strip topology
ps1bsp_triangle_t outTriangle;
outTriangle.vertex0 = faceVertIndices[0];
for (int faceVertIdx = 1; faceVertIdx < faceVertIndices.size() - 1; ++faceVertIdx)
{
outTriangle.vertex1 = faceVertIndices[faceVertIdx];
outTriangle.vertex2 = faceVertIndices[faceVertIdx + 1];
outTriangles.push_back(outTriangle);
}
// Write polygon and face data to a file
outFace.numTriangles = outTriangles.size() - outFace.firstTriangleId;
outFaces.push_back(outFace);
faceVerts.clear();
faceVertIndices.clear();
} }
// Write triangle and face data to file
fwrite(outTriangles.data(), sizeof(ps1bsp_triangle_t), outTriangles.size(), fbsp);
fwrite(outFaces.data(), sizeof(ps1bsp_face_t), outFaces.size(), fbsp);
// Update header information
outHeader.numVertices = world->numVertices;
outHeader.numTriangles = outTriangles.size();
outHeader.numFaces = outFaces.size();
// Write final header // Write final header
fseek(fbsp, 0, SEEK_SET); fseek(fbsp, 0, SEEK_SET);
fwrite(&outHeader, sizeof(ps1bsp_header_t), 1, fbsp); fwrite(&outHeader, sizeof(ps1bsp_header_t), 1, fbsp);
fclose(fbsp); fclose(fbsp);
printf("PS1BSP: wrote %d vertices, %d triangles, %d faces\n", outHeader.numVertices, outHeader.numTriangles, outHeader.numFaces);
return 1; return 1;
} }
@ -317,6 +362,15 @@ int load_bsp(const char* bspname, world_t* world)
world->textures[texNum * numMipLevels + mipLevel] = texBytes; world->textures[texNum * numMipLevels + mipLevel] = texBytes;
} }
} }
// Load planes
world->numPlanes = header->planes.size / sizeof(plane_t);
world->planes = (plane_t*)malloc(header->planes.size);
if (world->planes == NULL)
return 0;
fseek(f, header->planes.offset, SEEK_SET);
fread(world->planes, sizeof(plane_t), world->numPlanes, f);
// Load vertices // Load vertices
world->numVertices = header->vertices.size / sizeof(vertex_t); world->numVertices = header->vertices.size / sizeof(vertex_t);
@ -336,13 +390,13 @@ int load_bsp(const char* bspname, world_t* world)
fseek(f, header->edges.offset, SEEK_SET); fseek(f, header->edges.offset, SEEK_SET);
fread(world->edges, sizeof(edge_t), world->numEdges, f); fread(world->edges, sizeof(edge_t), world->numEdges, f);
world->edgeListLength = header->ledges.size / sizeof(unsigned short);
world->edgeList = (unsigned short*)malloc(header->ledges.size);
world->edgeListLength = header->ledges.size / sizeof(int);
world->edgeList = (int*)malloc(header->ledges.size);
if (world->edgeList == NULL) if (world->edgeList == NULL)
return 0; return 0;
fseek(f, header->ledges.offset, SEEK_SET); fseek(f, header->ledges.offset, SEEK_SET);
fread(world->edgeList, sizeof(unsigned short), world->edgeListLength, f);
fread(world->edgeList, sizeof(int), world->edgeListLength, f);
// Load faces // Load faces
world->numFaces = header->faces.size / sizeof(face_t); world->numFaces = header->faces.size / sizeof(face_t);
@ -358,9 +412,18 @@ int load_bsp(const char* bspname, world_t* world)
if (world->faceList == NULL) if (world->faceList == NULL)
return 0; return 0;
// Load visibility list
fseek(f, header->lface.offset, SEEK_SET); fseek(f, header->lface.offset, SEEK_SET);
fread(world->faceList, sizeof(unsigned short), world->faceListLength, f); fread(world->faceList, sizeof(unsigned short), world->faceListLength, f);
world->visListLength = header->visilist.size / sizeof(unsigned char);
world->visList = (unsigned char*)malloc(header->visilist.size);
if (world->visList == NULL)
return 0;
fseek(f, header->visilist.offset, SEEK_SET);
fread(world->visList, sizeof(unsigned char), world->visListLength, f);
// Load nodes // Load nodes
world->numNodes = header->nodes.size / sizeof(node_t); world->numNodes = header->nodes.size / sizeof(node_t);
world->nodes = (node_t*)malloc(header->nodes.size); world->nodes = (node_t*)malloc(header->nodes.size);
@ -387,12 +450,14 @@ void free_bsp(world_t* world)
{ {
free(world->leaves); free(world->leaves);
free(world->nodes); free(world->nodes);
free(world->visList);
free(world->faces); free(world->faces);
free(world->faceList); free(world->faceList);
free(world->edges); free(world->edges);
free(world->edgeList); free(world->edgeList);
free(world->vertices); free(world->vertices);
free(world->planes);
for (int i = 0; i < world->mipheader.numtex; ++i) for (int i = 0; i < world->mipheader.numtex; ++i)
{ {

9
ps1bsp.h

@ -49,11 +49,6 @@ typedef struct
unsigned char r : 5; unsigned char r : 5;
unsigned char g : 5; unsigned char g : 5;
unsigned char b : 5; unsigned char b : 5;
#ifdef _WIN32
// Extra fields used for administration during the conversion process
unsigned short index;
#endif
} ps1bsp_vertex_t; } ps1bsp_vertex_t;
// Instead of edges as in the original BSP format, we store triangles for easy consumption by the PS1 // Instead of edges as in the original BSP format, we store triangles for easy consumption by the PS1
@ -67,8 +62,8 @@ typedef struct
typedef struct typedef struct
{ {
unsigned short firstTriangleId;
unsigned short numTriangle;
unsigned short firstTriangleId; // TODO: could also just do first-index, num-indices here. No real need for a triangle_t struct.
unsigned short numTriangles;
unsigned short firstQuadId; // For if/when we decide to add quads to the mix unsigned short firstQuadId; // For if/when we decide to add quads to the mix
unsigned short numQuads; unsigned short numQuads;

Loading…
Cancel
Save