From c429529e88a8e288123ec05652ef179d1a94237f Mon Sep 17 00:00:00 2001 From: Nico de Poel Date: Mon, 16 Jan 2023 14:45:04 +0100 Subject: [PATCH] Fixed lightmap sampling: - Implement bounds calculation per face instead of for all faces, which was silly - Face lightmap width and height calculations now match what Quake does - Added an export function for lightmap data, to make debugging easier - Include face baselight into vertex lighting, if/when that ever makes a difference --- bsp.h | 2 +- main.cpp | 113 +++++++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 89 insertions(+), 26 deletions(-) diff --git a/bsp.h b/bsp.h index fa5fcdd..d75b54e 100644 --- a/bsp.h +++ b/bsp.h @@ -116,7 +116,7 @@ typedef struct Vertex float Y; // usually some integer value float Z; // but coded in floating point - vec3_t toVec() + vec3_t toVec() const { return vec3_t(X, Y, Z); } diff --git a/main.cpp b/main.cpp index 096b7a4..d61f768 100644 --- a/main.cpp +++ b/main.cpp @@ -172,42 +172,94 @@ static unsigned char sample_lightmap(const world_t* world, const face_t *face, c const unsigned char* lightmap = &world->lightmap[face->lightmap]; const plane_t* plane = &world->planes[face->plane_id]; - float w, h, u, v; + int width, height; + float u, v; switch (plane->type) { case 0: case 3: // Towards X - w = bounds.max.y - bounds.min.y; - h = bounds.max.z - bounds.min.z; - u = (point.y - bounds.min.y) / w; - v = (point.z - bounds.min.z) / h; + width = (int)(ceil(bounds.max.y / 16) - floor(bounds.min.y / 16)) * 16; + height = (int)(ceil(bounds.max.z / 16) - floor(bounds.min.z / 16)) * 16; + u = (point.y - bounds.min.y) / width; + v = (point.z - bounds.min.z) / height; break; case 1: case 4: // Towards Y - w = bounds.max.x - bounds.min.x; - h = bounds.max.z - bounds.min.z; - u = (point.x - bounds.min.x) / w; - v = (point.z - bounds.min.z) / h; + width = (int)(ceil(bounds.max.x / 16) - floor(bounds.min.x / 16)) * 16; + height = (int)(ceil(bounds.max.z / 16) - floor(bounds.min.z / 16)) * 16; + u = (point.x - bounds.min.x) / width; + v = (point.z - bounds.min.z) / height; break; case 2: case 5: // Towards Z - w = bounds.max.x - bounds.min.x; - h = bounds.max.y - bounds.min.y; - u = (point.x - bounds.min.x) / w; - v = (point.y - bounds.min.y) / h; + width = (int)(ceil(bounds.max.x / 16) - floor(bounds.min.x / 16)) * 16; + height = (int)(ceil(bounds.max.y / 16) - floor(bounds.min.y / 16)) * 16; + u = (point.x - bounds.min.x) / width; + v = (point.y - bounds.min.y) / height; break; default: printf("Error: unknown plane type %d\n", plane->type); return 0; } - int width = (int)(w / 16); - int height = (int)(h / 16); + height >>= 4; + width >>= 4; - return lightmap[(int)((height * v + u) * width)]; + return lightmap[(int)(v * (width + 1) + u)]; +} + +static void export_lightmap(const world_t* world, const face_t* face, const BoundBox& bounds, int faceIdx) +{ + if (face->lightmap < 0) + return; + + const unsigned char* lightmap = &world->lightmap[face->lightmap]; + const plane_t* plane = &world->planes[face->plane_id]; + + int width, height; + switch (plane->type) + { + case 0: + case 3: + // Towards X + width = ceil(bounds.max.y / 16) - floor(bounds.min.y / 16); + height = ceil(bounds.max.z / 16) - floor(bounds.min.z / 16); + break; + case 1: + case 4: + // Towards Y + width = ceil(bounds.max.x / 16) - floor(bounds.min.x / 16); + height = ceil(bounds.max.z / 16) - floor(bounds.min.z / 16); + break; + case 2: + case 5: + // Towards Z + width = ceil(bounds.max.x / 16) - floor(bounds.min.x / 16); + height = ceil(bounds.max.y / 16) - floor(bounds.min.y / 16); + break; + default: + printf("Error: unknown plane type %d\n", plane->type); + return; + } + + width += 1; + + char path[_MAX_PATH]; + sprintf_s(path, _MAX_PATH, "lightmap_face%d_e%d_PT%d_%dx%d.raw", faceIdx, face->ledge_num, plane->type, width, height); + FILE* flm; + fopen_s(&flm, path, "wb"); + if (!flm) + return; + + for (int y = 0; y < height; ++y) + { + fwrite(&lightmap[y * width], sizeof(unsigned char), width, flm); + } + + fclose(flm); } int process_faces(const world_t* world) @@ -231,17 +283,11 @@ int process_faces(const world_t* world) } // Write vertex data to a file (no vertex splitting yet) - BoundBox bounds; std::vector outVertices; for (unsigned short i = 0; i < world->numVertices; ++i) { // TODO: we should respect the ordering from vertexRef->index here but meh, problem for later vertex_t* inVertex = &world->vertices[i]; - - if (i == 0) - bounds.init(inVertex->toVec()); - else - bounds.includePoint(inVertex->toVec()); 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). @@ -270,6 +316,7 @@ int process_faces(const world_t* world) outFace.firstVertexIndex = faceVertIndices.size(); // Traverse the list of face edges to collect all of the face's vertices + BoundBox bounds; for (int edgeListIdx = 0; edgeListIdx < face->ledge_num; ++edgeListIdx) { int edgeIdx = world->edgeList[face->ledge_id + edgeListIdx]; @@ -280,14 +327,30 @@ int process_faces(const world_t* world) faceVertIndices.push_back(vertIndex); - unsigned char light = sample_lightmap(world, face, bounds, world->vertices[vertIndex].toVec()); - if (light > 0) + // Calculate bounding box of this face + const vertex_t* vertex = &world->vertices[vertIndex]; + if (edgeListIdx == 0) + bounds.init(vertex->toVec()); + else + bounds.includePoint(vertex->toVec()); + } + + // Accumulate lightmap contribution of this face on each vertex + if (face->lightmap >= 0) + { + for (int indexIdx = outFace.firstVertexIndex; indexIdx < faceVertIndices.size(); ++indexIdx) { - *(unsigned short*)(&outVertices[vertIndex].baseLight) += light; + int vertIndex = faceVertIndices[indexIdx]; + unsigned char light = sample_lightmap(world, face, bounds, world->vertices[vertIndex].toVec()); + *(unsigned short*)(&outVertices[vertIndex].baseLight) += light + (0xFF - face->baselight); outVertices[vertIndex].r++; } } + // For visualizing and debugging lightmaps + //if (face->ledge_num >= 10) + // export_lightmap(world, face, bounds, faceIdx); + outFace.numVertices = faceVertIndices.size() - outFace.firstVertexIndex; outFaces.push_back(outFace); }