diff --git a/common.h b/common.h index 868ed43..c5ef7dd 100644 --- a/common.h +++ b/common.h @@ -82,6 +82,29 @@ typedef struct Vec3 // Vector or Position } } vec3_t; +template<> struct std::hash +{ + std::uint64_t operator()(const Vec3& vec) const + { + int x = (int)(vec.x * 100); + int y = (int)(vec.y * 100); + int z = (int)(vec.z * 100); + + return + (*((uint64_t*)&x) << 40) | + (*((uint64_t*)&y) << 20) | + (*((uint64_t*)&z) << 0); + } +}; + +static bool operator==(const Vec3& lhs, const Vec3& rhs) +{ + return + fabs(rhs.x - lhs.x) < 0.001 && + fabs(rhs.y - lhs.y) < 0.001 && + fabs(rhs.z - lhs.z) < 0.001; +} + static float halton(int32_t index, int32_t base) { float f = 1.0f, result = 0.0f; diff --git a/main.cpp b/main.cpp index 0176e7c..cbf5bec 100644 --- a/main.cpp +++ b/main.cpp @@ -4,9 +4,12 @@ #include "ps1bsp.h" #include "lighting.h" #include "texture.h" +#include "gpc.h" static char path[_MAX_PATH]; +void gpc_test(const world_t* world, const face_t* face); + template size_t writeMapData(const std::vector& data, ps1bsp_dentry_t& dentry, FILE* f) { dentry.offset = (unsigned int)ftell(f); @@ -204,6 +207,8 @@ int process_faces(const world_t* world, const std::vector& tex float area = computeFaceArea(world, face); outFace.center.pad = (short)(sqrt(area)); outFaces.push_back(outFace); + + gpc_test(world, face); } // Iterate over all faces again; now that we know the bounds of each face, we can calculate lighting for all of them @@ -218,7 +223,7 @@ int process_faces(const world_t* world, const std::vector& tex ps1bsp_facevertex_t& faceVertex = outFaceVertices[outFace.firstFaceVertex + faceVertIdx]; const vertex_t* vertex = &world->vertices[faceVertex.index]; faceVertex.light = compute_faceVertex_light5(world, face, faceBounds, vertex->toVec()); - faceVertex.light = (short)((float)faceVertex.light * 1.5f); + faceVertex.light = (short)((float)faceVertex.light * 1.5f); // Compromise between overbright and non-overbright lighting. Looks good in practice. if (faceVertex.light > 255) faceVertex.light = 255; } @@ -298,6 +303,101 @@ int process_faces(const world_t* world, const std::vector& tex return 1; } +void gpc_test(const world_t* world, const face_t* face) +{ + 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]; + + double minS = DBL_MAX, minT = DBL_MAX; + double maxS = DBL_MIN, maxT = DBL_MIN; + + gpc_polygon polygon = { 0 }; + gpc_vertex_list contour; + contour.num_vertices = face->ledge_num; + contour.vertex = (gpc_vertex*)malloc(contour.num_vertices * sizeof(gpc_vertex)); + if (contour.vertex == NULL) + return; + + std::vector originalVecs; + for (int edgeListIdx = 0; edgeListIdx < face->ledge_num; ++edgeListIdx) + { + int edgeIdx = world->edgeList[face->ledge_id + edgeListIdx]; + + unsigned short vertIndex = edgeIdx > 0 ? + world->edges[edgeIdx].vertex0 : + world->edges[-edgeIdx].vertex1; + + const vertex_t* vertex = &world->vertices[vertIndex]; + Vec3 vertexPoint = vertex->toVec(); + originalVecs.push_back(vertexPoint); + + // Calculate texture UV bounds + double s = (vertexPoint.dotProduct(texinfo->vectorS) + texinfo->distS) / miptex->width; + double t = (vertexPoint.dotProduct(texinfo->vectorT) + texinfo->distT) / miptex->height; + if (s > maxS) maxS = s; if (s < minS) minS = s; + if (t > maxT) maxT = t; if (t < minT) minT = t; + + contour.vertex[edgeListIdx] = gpc_vertex{ s, t }; + } + + gpc_add_contour(&polygon, &contour, 0); + + std::vector vertices; + std::unordered_map vertexIndices; + + // Create a virtual grid at the texture bounds and iterate over each cell to break up the face into repeating tiles + for (double y = floor(minT); y <= ceil(maxT); y += 1.0) + { + for (double x = floor(minS); x <= ceil(maxS); x += 1.0) + { + // Create a square polygon that covers the entire cell + gpc_polygon cell = { 0 }; + gpc_vertex_list cell_bounds; + cell_bounds.num_vertices = 4; + cell_bounds.vertex = (gpc_vertex*)malloc(4 * sizeof(gpc_vertex)); + cell_bounds.vertex[0] = gpc_vertex{ x, y }; + cell_bounds.vertex[1] = gpc_vertex{ x + 1.0, y }; + cell_bounds.vertex[2] = gpc_vertex{ x + 1.0, y + 1.0 }; + cell_bounds.vertex[3] = gpc_vertex{ x, y + 1.0 }; + gpc_add_contour(&cell, &cell_bounds, 0); + + // Take the intersection to get the chunk of the face that's inside this cell + gpc_polygon result; + gpc_polygon_clip(GPC_INT, &polygon, &cell, &result); + + // We should get a polygon with exactly one contour as a result; if not, the face was not on this grid cell + if (result.num_contours <= 0) + continue; + + // Reconstruct the polygon's vertices in world space + for (int v = 0; v < result.contour[0].num_vertices; ++v) + { + const auto vert = &result.contour[0].vertex[v]; + Vec3 newVert = + plane->normal * plane->dist + + texinfo->vectorS * (float)(vert->x * miptex->width - texinfo->distS) + + texinfo->vectorT * (float)(vert->y * miptex->height - texinfo->distT); + + // Make sure we don't store duplicate vertices + auto vertIter = vertexIndices.find(newVert); + if (vertIter == vertexIndices.end()) + { + vertexIndices[newVert] = vertices.size(); + vertices.push_back(newVert); + } + + // Store the relevant (S, T) coordinates for each vertex-texinfo pair + } + + gpc_free_polygon(&result); + gpc_free_polygon(&cell); + } + } + + gpc_free_polygon(&polygon); +} + int process_bsp(const world_t *world) { // Test exporting texture data