diff --git a/lighting.cpp b/lighting.cpp index 64a755b..187b4e0 100644 --- a/lighting.cpp +++ b/lighting.cpp @@ -1,7 +1,7 @@ #include "common.h" #include "lighting.h" -bool sample_lightmap(const world_t* world, const face_t* face, const BoundBox& bounds, const Vec3& point, unsigned char* outSample) +bool sample_lightmap(const world_t* world, const face_t* face, const FaceBound& bounds, const Vec3& point, unsigned char* outSample) { if (face->lightmap < 0) return false; @@ -9,8 +9,8 @@ bool sample_lightmap(const world_t* world, const face_t* face, const BoundBox& b const unsigned char* lightmap = &world->lightmap[face->lightmap]; const plane_t* plane = &world->planes[face->plane_id]; - Vec3 minBounds = (bounds.min / 16).floor() * 16.f; - Vec3 maxBounds = (bounds.max / 16).ceil() * 16.f; + Vec3 minBounds = (bounds.worldBounds.min / 16).floor() * 16.f; + Vec3 maxBounds = (bounds.worldBounds.max / 16).ceil() * 16.f; int width, height; int u, v; @@ -52,7 +52,7 @@ bool sample_lightmap(const world_t* world, const face_t* face, const BoundBox& b return true; } -unsigned char sample_lightmap(const world_t* world, const face_t* face, const BoundBox& bounds, const Vec3& point) +unsigned char sample_lightmap(const world_t* world, const face_t* face, const FaceBound& bounds, const Vec3& point) { unsigned char sample; if (!sample_lightmap(world, face, bounds, point, &sample)) @@ -61,7 +61,7 @@ unsigned char sample_lightmap(const world_t* world, const face_t* face, const Bo return sample; } -void export_lightmap(const world_t* world, const face_t* face, const BoundBox& bounds, int faceIdx) +void export_lightmap(const world_t* world, const face_t* face, const FaceBound& bounds, int faceIdx) { if (face->lightmap < 0) return; @@ -75,20 +75,20 @@ void export_lightmap(const world_t* world, const face_t* face, const BoundBox& b case 0: case 3: // Towards X - width = (int)(ceil(bounds.max.y / 16) - floor(bounds.min.y / 16)); - height = (int)(ceil(bounds.max.z / 16) - floor(bounds.min.z / 16)); + width = (int)(ceil(bounds.worldBounds.max.y / 16) - floor(bounds.worldBounds.min.y / 16)); + height = (int)(ceil(bounds.worldBounds.max.z / 16) - floor(bounds.worldBounds.min.z / 16)); break; case 1: case 4: // Towards Y - width = (int)(ceil(bounds.max.x / 16) - floor(bounds.min.x / 16)); - height = (int)(ceil(bounds.max.z / 16) - floor(bounds.min.z / 16)); + width = (int)(ceil(bounds.worldBounds.max.x / 16) - floor(bounds.worldBounds.min.x / 16)); + height = (int)(ceil(bounds.worldBounds.max.z / 16) - floor(bounds.worldBounds.min.z / 16)); break; case 2: case 5: // Towards Z - width = (int)(ceil(bounds.max.x / 16) - floor(bounds.min.x / 16)); - height = (int)(ceil(bounds.max.y / 16) - floor(bounds.min.y / 16)); + width = (int)(ceil(bounds.worldBounds.max.x / 16) - floor(bounds.worldBounds.min.x / 16)); + height = (int)(ceil(bounds.worldBounds.max.y / 16) - floor(bounds.worldBounds.min.y / 16)); break; default: printf("Error: unknown plane type %d\n", plane->type); diff --git a/lighting.h b/lighting.h index b010a79..7e3b833 100644 --- a/lighting.h +++ b/lighting.h @@ -15,13 +15,28 @@ struct Surface std::unordered_set faces; }; -typedef std::unordered_map FaceBounds; +struct FaceBound +{ + BoundBox worldBounds; + double minS = DBL_MAX, minT = DBL_MAX; + double maxS = DBL_MIN, maxT = DBL_MIN; + + void addTexturePoint(double s, double t) + { + if (s < minS) minS = s; + if (s > maxS) maxS = s; + if (t < minT) minT = t; + if (t > maxT) maxT = t; + } +}; + +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); +bool sample_lightmap(const world_t* world, const face_t* face, const FaceBound& bounds, const Vec3& point, unsigned char* outSample); +unsigned char sample_lightmap(const world_t* world, const face_t* face, const FaceBound& bounds, const Vec3& point); +void export_lightmap(const world_t* world, const face_t* face, const FaceBound& bounds, int faceIdx); std::unordered_map analyze_edges(const world_t* world); unsigned char compute_faceVertex_light(const world_t* world, const face_t* face, unsigned short vertexIndex, const FaceBounds& faceBounds, const std::unordered_map& edgeData); diff --git a/main.cpp b/main.cpp index ec57833..16665e4 100644 --- a/main.cpp +++ b/main.cpp @@ -68,6 +68,8 @@ int process_faces(const world_t* world, const std::vector& tex { const face_t* face = &world->faces[faceIdx]; const texinfo_t* texinfo = &world->texInfos[face->texinfo_id]; + const miptex_t* miptex = &world->miptexes[texinfo->texture_id]; + const plane_t* plane = &world->planes[face->plane_id]; ps1bsp_face_t outFace = { 0 }; outFace.planeId = face->plane_id; @@ -75,9 +77,11 @@ int process_faces(const world_t* world, const std::vector& tex outFace.firstFaceVertex = (unsigned short)outFaceVertices.size(); outFace.textureId = (unsigned char)texinfo->texture_id; + Matrix4x4 textureTrsf = tesselator.buildTextureSpaceTransform(texinfo, miptex, plane); + // Traverse the list of face edges to collect all of the face's vertices Vec3 vertexSum; - BoundBox bounds; + FaceBound bounds; for (int edgeListIdx = 0; edgeListIdx < face->ledge_num; ++edgeListIdx) { int edgeIdx = world->edgeList[face->ledge_id + edgeListIdx]; @@ -91,9 +95,12 @@ int process_faces(const world_t* world, const std::vector& tex // Calculate bounding box of this face if (edgeListIdx == 0) - bounds.init(vertexPoint); + bounds.worldBounds.init(vertexPoint); else - bounds.includePoint(vertexPoint); + bounds.worldBounds.includePoint(vertexPoint); + + Vec3 texturePoint = textureTrsf.TransformPoint(vertexPoint); + bounds.addTexturePoint(texturePoint.x, texturePoint.y); // Sum all vertices to calculate an average center point vertexSum = vertexSum + vertexPoint; @@ -163,13 +170,12 @@ int process_faces(const world_t* world, const std::vector& tex size_t vertIndex = polyVertIter->vertexIndex; Vec3 normalizedUV = polyVertIter->normalizedUV; - Vec3 vertex = tesselator.getVertices()[vertIndex]; - 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; + Vec3 vertex = tesselator.getVertices()[vertIndex]; int light = compute_faceVertex_light5(world, face, faceBounds, vertex); light = (int)((float)light * 1.5f); // Compromise between overbright and non-overbright lighting. Looks good in practice. if (light > 255) diff --git a/tesselate.cpp b/tesselate.cpp index a7fbd61..c6ce455 100644 --- a/tesselate.cpp +++ b/tesselate.cpp @@ -22,14 +22,7 @@ std::vector Tesselator::tesselateFace(const face_t* face) double minS = DBL_MAX, minT = DBL_MAX; double maxS = DBL_MIN, maxT = DBL_MIN; - // vectorS and vectorT are normally perpendicular (dot product is 0), magnitude isn't always 1 but that's fine. - // Means we can construct a coordinate space from them (plane normal for the third vector) and transform the vertices to texture space. - // And we can create an inverse transform, meaning we can transform vertices from 2D texture space back to 3D world space. - Matrix4x4 textureTrsf; - textureTrsf.SetAxis(0, texinfo->vectorS / (float)miptex->width); - textureTrsf.SetAxis(1, texinfo->vectorT / (float)miptex->height); - textureTrsf.SetAxis(2, plane->normal); - textureTrsf.SetTranslation(Vec3(texinfo->distS / miptex->width, texinfo->distT / miptex->height, -plane->dist)); + Matrix4x4 textureTrsf = buildTextureSpaceTransform(texinfo, miptex, plane); // Build a polygon in normalized 2D texture space from the original face data std::vector faceVertices; @@ -112,3 +105,16 @@ std::vector Tesselator::tesselateFace(const face_t* face) gpc_free_polygon(&facePolygon); return polygons; } + +Matrix4x4 Tesselator::buildTextureSpaceTransform(const texinfo_t* texinfo, const miptex_t* miptex, const plane_t* plane) +{ + // vectorS and vectorT are normally perpendicular (dot product is 0), magnitude isn't always 1 but that's fine. + // Means we can construct a coordinate space from them (plane normal for the third vector) and transform the vertices to texture space. + // And we can create an inverse transform, meaning we can transform vertices from 2D texture space back to 3D world space. + Matrix4x4 matrix; + matrix.SetAxis(0, texinfo->vectorS / (float)miptex->width); + matrix.SetAxis(1, texinfo->vectorT / (float)miptex->height); + matrix.SetAxis(2, plane->normal); + matrix.SetTranslation(Vec3(texinfo->distS / miptex->width, texinfo->distT / miptex->height, -plane->dist)); + return matrix; +} diff --git a/tesselate.h b/tesselate.h index 128ae64..b8f6d1e 100644 --- a/tesselate.h +++ b/tesselate.h @@ -1,4 +1,5 @@ #pragma once +#include "matrix.h" class Tesselator { @@ -46,6 +47,8 @@ public: std::vector tesselateFace(const face_t* face); + static Matrix4x4 buildTextureSpaceTransform(const texinfo_t* texinfo, const miptex_t* miptex, const plane_t* plane); + private: const world_t* world;