From 161ef0aceea2c39dc6dbfa56f993daa10144d157 Mon Sep 17 00:00:00 2001 From: Nico de Poel Date: Tue, 24 Jan 2023 21:32:55 +0100 Subject: [PATCH] Improved lightmap sampling accuracy. The generated UVs now more closely match what Quake is doing, we can reject invalid UVs, and we're getting less false rejects of valid faces to sample from. --- bsp.h | 6 +++--- common.h | 10 +++++++++ lighting.cpp | 58 +++++++++++++++++++++++++++++++++++----------------- lighting.h | 1 + 4 files changed, 53 insertions(+), 22 deletions(-) diff --git a/bsp.h b/bsp.h index 0d85329..1198e59 100644 --- a/bsp.h +++ b/bsp.h @@ -239,7 +239,7 @@ typedef struct World // Check if the point lies on the face's plane (it's not strictly necessary to do this, but this check makes the whole function a lot faster) double pointPlaneDist = ((double)point.x * plane->normal.x + (double)point.y * plane->normal.y + (double)point.z * plane->normal.z) - plane->dist; - if (fabs(pointPlaneDist) > 0.0001) + if (fabs(pointPlaneDist) > 0.001) continue; // Check if the point is contained within the face's polygon @@ -267,7 +267,7 @@ typedef struct World double m0 = p0.magnitude(); double m1 = p1.magnitude(); - if ((m0 * m1) <= 0.0001) + if ((m0 * m1) <= 0.001) { // Point is on one of the vertices outFaces.push_back(face); @@ -279,7 +279,7 @@ typedef struct World } // If the point is inside the polygon, then the sum of all the above angles will be exactly 360 degrees - if (fabs(2 * M_PI - angleSum) <= 0.0001) + if (fabs(2 * M_PI - angleSum) <= 0.001) outFaces.push_back(face); } diff --git a/common.h b/common.h index 188222f..ab9d029 100644 --- a/common.h +++ b/common.h @@ -65,4 +65,14 @@ typedef struct Vec3 // Vector or Position { return Vec3(y * other.z - z * other.y, z * other.x - x * other.z, x * other.y - y * other.x); } + + Vec3 floor() const + { + return Vec3(floorf(x), floorf(y), floorf(z)); + } + + Vec3 ceil() const + { + return Vec3(ceilf(x), ceilf(y), ceilf(z)); + } } vec3_t; diff --git a/lighting.cpp b/lighting.cpp index 0d3544e..0a4e7e4 100644 --- a/lighting.cpp +++ b/lighting.cpp @@ -1,51 +1,64 @@ #include "common.h" #include "lighting.h" -unsigned char sample_lightmap(const world_t* world, const face_t* face, const BoundBox& bounds, const Vec3& point) +bool sample_lightmap(const world_t* world, const face_t* face, const BoundBox& bounds, const Vec3& point, unsigned char* outSample) { if (face->lightmap < 0) - return 0; + return false; const unsigned char* lightmap = &world->lightmap[face->lightmap]; const plane_t* plane = &world->planes[face->plane_id]; + Vec3 minBounds = (bounds.min / 16).floor() * 16; + Vec3 maxBounds = (bounds.max / 16).ceil() * 16; + int width, height; - float u, v; + int u, v; switch (plane->type) { case 0: case 3: // Towards X - 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) / (bounds.max.y - bounds.min.y); - v = (point.z - bounds.min.z) / (bounds.max.z - bounds.min.z); + width = (int)(maxBounds.y - minBounds.y); + height = (int)(maxBounds.z - minBounds.z); + u = (int)(point.y - minBounds.y); + v = (int)(point.z - minBounds.z); break; case 1: case 4: // Towards Y - 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) / (bounds.max.x - bounds.min.x); - v = (point.z - bounds.min.z) / (bounds.max.z - bounds.min.z); + width = (int)(maxBounds.x - minBounds.x); + height = (int)(maxBounds.z - minBounds.z); + u = (int)(point.x - minBounds.x); + v = (int)(point.z - minBounds.z); break; case 2: case 5: // Towards Z - 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) / (bounds.max.x - bounds.min.x); - v = (point.y - bounds.min.y) / (bounds.max.y - bounds.min.y); + width = (int)(maxBounds.x - minBounds.x); + height = (int)(maxBounds.y - minBounds.y); + u = (int)(point.x - minBounds.x); + v = (int)(point.y - minBounds.y); break; default: printf("Error: unknown plane type %d\n", plane->type); return 0; } - height >>= 4; - width >>= 4; + if (u < 0 || v < 0 || u > width || v > height) + return false; - return lightmap[(int)(v * (width + 1) + u)]; + *outSample = lightmap[(v >> 4) * (width >> 4) + (u >> 4)]; + return true; +} + +unsigned char sample_lightmap(const world_t* world, const face_t* face, const BoundBox& bounds, const Vec3& point) +{ + unsigned char sample; + if (!sample_lightmap(world, face, bounds, point, &sample)) + return 0; + + return sample; } void export_lightmap(const world_t* world, const face_t* face, const BoundBox& bounds, int faceIdx) @@ -327,6 +340,9 @@ SurfaceList group_surfaces(const world_t* world, const VertexFaces& vertexFaces) unsigned char compute_faceVertex_light4(const world_t* world, const face_t* refFace, const FaceBounds& faceBounds, Vec3 point) { + if (refFace->lightmap < 0) + return 0; + auto faces = world->facesWithPoint(point); if (faces.empty()) return 0; @@ -346,7 +362,11 @@ unsigned char compute_faceVertex_light4(const world_t* world, const face_t* refF if (dot < 0.5f) continue; - light += sample_lightmap(world, face, faceBounds.find(face)->second, point) + (0xFF - face->baselight); + unsigned char sample; + if (!sample_lightmap(world, face, faceBounds.find(face)->second, point, &sample)) + continue; + + light += sample + (0xFF - face->baselight); numSamples++; } diff --git a/lighting.h b/lighting.h index 066813a..75ea3f7 100644 --- a/lighting.h +++ b/lighting.h @@ -19,6 +19,7 @@ typedef std::unordered_map FaceBounds; typedef std::unordered_map> VertexFaces; typedef std::vector SurfaceList; +bool sample_lightmap(const world_t* world, const face_t* face, const BoundBox& bounds, const Vec3& point, unsigned char* outSample); unsigned char sample_lightmap(const world_t* world, const face_t* face, const BoundBox& bounds, const Vec3& point); void export_lightmap(const world_t* world, const face_t* face, const BoundBox& bounds, int faceIdx);