From 83dbee34e5a39cf0a49ecd8861c2f1a3002b2c55 Mon Sep 17 00:00:00 2001 From: Nico de Poel Date: Sun, 12 Feb 2023 14:39:07 +0100 Subject: [PATCH] Use GPC to get rid of redundant vertices on some faces, which will reduce a bunch of them down to simple quads. This makes for slightly less additional vertex data required when storing bounds. --- face.cpp | 46 ++++++++++++++++++++++++++++++++++++++++++++++ face.h | 1 + main.cpp | 2 +- 3 files changed, 48 insertions(+), 1 deletion(-) diff --git a/face.cpp b/face.cpp index 675c882..dda9e87 100644 --- a/face.cpp +++ b/face.cpp @@ -1,6 +1,8 @@ #include "common.h" +#include "matrix.h" #include "bsp.h" #include "face.h" +#include "gpc.h" // Heron's formula, yoinked from: https://www.cuemath.com/measurement/area-of-triangle/ static double face_triangleArea(Vec3 v0, Vec3 v1, Vec3 v2) @@ -52,3 +54,47 @@ double face_computeArea(const world_t* world, const face_t* face) return totalArea; } + +std::vector face_optimizeGeometry(const std::vector faceVertices, Matrix4x4 textureTransform) +{ + std::vector result; + + // Some faces have redundant/degenerate vertices. We can optimize those by passing their polygons through GPC. + gpc_polygon facePolygon = { 0 }; + gpc_vertex_list contour; + contour.num_vertices = faceVertices.size(); + contour.vertex = (gpc_vertex*)malloc(contour.num_vertices * sizeof(gpc_vertex)); + if (contour.vertex == NULL) + return result; + + int idx = 0; + for (auto vertIter = faceVertices.cbegin(); vertIter != faceVertices.cend(); ++vertIter) + { + Vec3 st = textureTransform.TransformPoint(*vertIter); + contour.vertex[idx++] = gpc_vertex{ st.x, st.y }; + } + + gpc_add_contour(&facePolygon, &contour, 0); + + // Intersect the face's polygon with itself. That should get rid of any redundant vertices. + gpc_polygon optimized = { 0 }; + gpc_polygon_clip(GPC_INT, &facePolygon, &facePolygon, &optimized); + + if (optimized.num_contours < 1) + return result; + + // Transform the optimized polygon back to world space + textureTransform.Invert(); + + gpc_vertex_list* newContour = &optimized.contour[0]; + for (int i = 0; i < newContour->num_vertices; ++i) + { + Vec3 st = Vec3(newContour->vertex[i].x, newContour->vertex[i].y, 0.0); + Vec3 vert = textureTransform.TransformPoint(st); + result.push_back(vert); + } + + gpc_free_polygon(&optimized); + gpc_free_polygon(&facePolygon); + return result; +} diff --git a/face.h b/face.h index 4308d91..e7eae34 100644 --- a/face.h +++ b/face.h @@ -1,3 +1,4 @@ #pragma once double face_computeArea(const world_t* world, const face_t* face); +std::vector face_optimizeGeometry(const std::vector faceVertices, Matrix4x4 textureTransform); diff --git a/main.cpp b/main.cpp index b13066e..61225cc 100644 --- a/main.cpp +++ b/main.cpp @@ -98,7 +98,6 @@ int process_faces(const world_t* world, const TextureList& textures) world->edges[edgeIdx].vertex0 : world->edges[-edgeIdx].vertex1; - // TODO: some faces have redundant/degenerate vertices. We could optimize those by passing their polygons through GPC. const vertex_t* vertex = &world->vertices[vertIndex]; Vec3 vertexPoint = vertex->toVec(); faceVertices.push_back(vertexPoint); @@ -121,6 +120,7 @@ int process_faces(const world_t* world, const TextureList& textures) // export_lightmap(world, face, bounds, faceIdx); // Determine the face's bounding rectangle in world space (only four vertices, on the face's plane) + faceVertices = face_optimizeGeometry(faceVertices, bounds.textureTransform); switch (faceVertices.size()) { case 3: // Special case: a triangle