From c111a2090c180f124e824ce9cac2bf6ed387a0e7 Mon Sep 17 00:00:00 2001 From: Nico de Poel Date: Thu, 9 Feb 2023 10:56:39 +0100 Subject: [PATCH] Implemented a more accurate face area computation, allowing for more precise tessellation LOD selection. Also started putting face-related functions into a separate source file. --- PS1BSP.vcxproj | 2 ++ PS1BSP.vcxproj.filters | 6 +++++ face.cpp | 54 ++++++++++++++++++++++++++++++++++++++++++ face.h | 3 +++ main.cpp | 34 ++++---------------------- 5 files changed, 69 insertions(+), 30 deletions(-) create mode 100644 face.cpp create mode 100644 face.h diff --git a/PS1BSP.vcxproj b/PS1BSP.vcxproj index bb52a8d..848b7a8 100644 --- a/PS1BSP.vcxproj +++ b/PS1BSP.vcxproj @@ -142,6 +142,7 @@ + @@ -152,6 +153,7 @@ + diff --git a/PS1BSP.vcxproj.filters b/PS1BSP.vcxproj.filters index eb8a372..e7b3a06 100644 --- a/PS1BSP.vcxproj.filters +++ b/PS1BSP.vcxproj.filters @@ -36,6 +36,9 @@ Source Files + + Source Files + @@ -86,6 +89,9 @@ Header Files + + Header Files + diff --git a/face.cpp b/face.cpp new file mode 100644 index 0000000..675c882 --- /dev/null +++ b/face.cpp @@ -0,0 +1,54 @@ +#include "common.h" +#include "bsp.h" +#include "face.h" + +// Heron's formula, yoinked from: https://www.cuemath.com/measurement/area-of-triangle/ +static double face_triangleArea(Vec3 v0, Vec3 v1, Vec3 v2) +{ + double a = (v1 - v0).magnitude(); + double b = (v2 - v0).magnitude(); + double c = (v0 - v2).magnitude(); + double s = (a + b + c) * 0.5; + + double areaSqr = s * (s - a) * (s - b) * (s - c); + if (areaSqr < FLT_EPSILON) // Collapsed triangles can happen, prevent NaN in that situation + return 0.0; + + return sqrt(areaSqr); +} + +double face_computeArea(const world_t* world, const face_t* face) +{ + int i0, i1, i2; + + // Get the first edge so we can build a triangle fan + int edgeIdx = world->edgeList[face->ledge_id]; + if (edgeIdx > 0) + { + i0 = world->edges[edgeIdx].vertex0; + i1 = world->edges[edgeIdx].vertex1; + } + else + { + i0 = world->edges[-edgeIdx].vertex1; + i1 = world->edges[-edgeIdx].vertex0; + } + + // Triangulate the face, compute the area of each triangle and add them all together + double totalArea = 0.0; + for (int edgeListIdx = 1; edgeListIdx < face->ledge_num - 1; ++edgeListIdx) + { + int edgeIdx = world->edgeList[face->ledge_id + edgeListIdx]; + i2 = edgeIdx > 0 ? world->edges[edgeIdx].vertex0 : world->edges[-edgeIdx].vertex1; + + Vec3 v0 = world->vertices[i0].toVec(); + Vec3 v1 = world->vertices[i1].toVec(); + Vec3 v2 = world->vertices[i2].toVec(); + + totalArea += face_triangleArea(v0, v1, v2); + + i1 = i2; + } + + return totalArea; +} diff --git a/face.h b/face.h new file mode 100644 index 0000000..4308d91 --- /dev/null +++ b/face.h @@ -0,0 +1,3 @@ +#pragma once + +double face_computeArea(const world_t* world, const face_t* face); diff --git a/main.cpp b/main.cpp index c3c4452..062bc13 100644 --- a/main.cpp +++ b/main.cpp @@ -4,6 +4,7 @@ #include "ps1bsp.h" #include "lighting.h" #include "texture.h" +#include "face.h" #include "tesselate.h" static char path[_MAX_PATH]; @@ -43,35 +44,6 @@ size_t writeAtlasData(const char* timPath, ps1bsp_dentry_t& dentry, FILE* f) return written; } -static float computeFaceArea(const world_t* world, const face_t* face) -{ - const plane_t* plane = &world->planes[face->plane_id]; - - // Construct a tangent and bitangent for the plane - Vec3 tangent, bitangent; - plane->getTangents(tangent, bitangent); - - // Project all face vertices onto the face's plane - BoundBox bounds; - for (int edgeListIdx = 0; edgeListIdx < face->ledge_num; ++edgeListIdx) - { - int edgeIdx = world->edgeList[face->ledge_id + edgeListIdx]; - int vertIndex = edgeIdx > 0 ? - world->edges[edgeIdx].vertex0 : - world->edges[-edgeIdx].vertex1; - - const vertex_t* vertex = &world->vertices[vertIndex]; - Vec3 vec = vertex->toVec(); - - double x = tangent.dotProduct(vec); - double y = bitangent.dotProduct(vec); - bounds.includePoint(Vec3(x, y, 0.0)); - } - - Vec3 extents = bounds.max - bounds.min; - return extents.x * extents.y; -} - typedef std::unordered_map> FacePolygons; int process_faces(const world_t* world, const TextureList& textures) @@ -156,7 +128,7 @@ int process_faces(const world_t* world, const TextureList& textures) outFace.numFaceVertices = (unsigned char)(outFaceVertices.size() - outFace.firstFaceVertex); outFace.center = (vertexSum / face->ledge_num).convertWorldPosition(); - float area = computeFaceArea(world, face); + double area = face_computeArea(world, face); outFace.center.pad = (short)(area / 100); outFaces.push_back(outFace); } @@ -290,6 +262,8 @@ int process_faces(const world_t* world, const TextureList& textures) outFace->numPolygons = (unsigned char)(outPolygons.size() - outFace->firstPolygon); outFace->center.pad = (short)sqrt((double)outFace->center.pad * 100 / outFace->numPolygons); + if (outFace->center.pad <= 0) + outFace->center.pad = 1; // Prevent division by zero in the LOD calculation } printf("Converting data...\n");