Browse Source

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.

master
Nico de Poel 3 years ago
parent
commit
83dbee34e5
  1. 46
      face.cpp
  2. 1
      face.h
  3. 2
      main.cpp

46
face.cpp

@ -1,6 +1,8 @@
#include "common.h" #include "common.h"
#include "matrix.h"
#include "bsp.h" #include "bsp.h"
#include "face.h" #include "face.h"
#include "gpc.h"
// Heron's formula, yoinked from: https://www.cuemath.com/measurement/area-of-triangle/ // Heron's formula, yoinked from: https://www.cuemath.com/measurement/area-of-triangle/
static double face_triangleArea(Vec3 v0, Vec3 v1, Vec3 v2) 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; return totalArea;
} }
std::vector<Vec3> face_optimizeGeometry(const std::vector<Vec3> faceVertices, Matrix4x4 textureTransform)
{
std::vector<Vec3> 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;
}

1
face.h

@ -1,3 +1,4 @@
#pragma once #pragma once
double face_computeArea(const world_t* world, const face_t* face); double face_computeArea(const world_t* world, const face_t* face);
std::vector<Vec3> face_optimizeGeometry(const std::vector<Vec3> faceVertices, Matrix4x4 textureTransform);

2
main.cpp

@ -98,7 +98,6 @@ int process_faces(const world_t* world, const TextureList& textures)
world->edges[edgeIdx].vertex0 : world->edges[edgeIdx].vertex0 :
world->edges[-edgeIdx].vertex1; 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]; const vertex_t* vertex = &world->vertices[vertIndex];
Vec3 vertexPoint = vertex->toVec(); Vec3 vertexPoint = vertex->toVec();
faceVertices.push_back(vertexPoint); faceVertices.push_back(vertexPoint);
@ -121,6 +120,7 @@ int process_faces(const world_t* world, const TextureList& textures)
// export_lightmap(world, face, bounds, faceIdx); // export_lightmap(world, face, bounds, faceIdx);
// Determine the face's bounding rectangle in world space (only four vertices, on the face's plane) // 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()) switch (faceVertices.size())
{ {
case 3: // Special case: a triangle case 3: // Special case: a triangle

Loading…
Cancel
Save