change face output to be slightly more robust

fix tjunc multithreading; added `original_vertices` which is a copy of the `output_vertices` that is safe for tjunc to read from since tjunc writes to the former
This commit is contained in:
Jonathan 2022-07-14 01:56:18 -04:00
parent a435f67f13
commit ae4c5cd365
4 changed files with 42 additions and 32 deletions

View File

@ -324,7 +324,7 @@ class mapentity_t;
struct face_fragment_t struct face_fragment_t
{ {
std::vector<size_t> output_vertices; // filled in by EmitVertices & TJunc std::vector<size_t> output_vertices, original_vertices; // filled in by EmitVertices & TJunc
std::vector<int64_t> edges; // only filled in MakeFaceEdges std::vector<int64_t> edges; // only filled in MakeFaceEdges
std::optional<size_t> outputnumber; // only valid for original faces after std::optional<size_t> outputnumber; // only valid for original faces after
// write surfaces // write surfaces

View File

@ -82,6 +82,8 @@ static void EmitFaceVertices(face_t *f)
for (size_t i = 0; i < f->w.size(); i++) { for (size_t i = 0; i < f->w.size(); i++) {
EmitVertex(f->w[i], f->output_vertices[i]); EmitVertex(f->w[i], f->output_vertices[i]);
} }
f->original_vertices = f->output_vertices;
} }
static void EmitVertices_R(node_t *node) static void EmitVertices_R(node_t *node)
@ -136,7 +138,7 @@ inline int64_t GetEdge(const size_t &v1, const size_t &v2, const face_t *face)
static void FindFaceFragmentEdges(face_t *face, face_fragment_t *fragment) static void FindFaceFragmentEdges(face_t *face, face_fragment_t *fragment)
{ {
fragment->outputnumber = std::nullopt; Q_assert(fragment->outputnumber == std::nullopt);
if (fragment->output_vertices.size() > MAXEDGES) { if (fragment->output_vertices.size() > MAXEDGES) {
FError("Internal error: face->numpoints > MAXEDGES"); FError("Internal error: face->numpoints > MAXEDGES");

View File

@ -125,7 +125,7 @@ static void FindEdgeVerts_FaceBounds_R(const node_t *node, const aabb3d &aabb, s
} }
for (auto &face : node->facelist) { for (auto &face : node->facelist) {
for (auto &v : face->output_vertices) { for (auto &v : face->original_vertices) {
if (aabb.containsPoint(map.bsp.dvertexes[v])) { if (aabb.containsPoint(map.bsp.dvertexes[v])) {
verts.push_back(v); verts.push_back(v);
} }
@ -143,7 +143,7 @@ FindEdgeVerts_FaceBounds
Use a loose AABB around the line and only capture vertices that intersect it. Use a loose AABB around the line and only capture vertices that intersect it.
========== ==========
*/ */
static void FindEdgeVerts_FaceBounds(const node_t *headnode, const node_t *node, const qvec3d &p1, const qvec3d &p2, std::vector<size_t> &verts) static void FindEdgeVerts_FaceBounds(const node_t *headnode, const qvec3d &p1, const qvec3d &p2, std::vector<size_t> &verts)
{ {
// magic number, average of "usual" points per edge // magic number, average of "usual" points per edge
verts.reserve(8); verts.reserve(8);
@ -162,7 +162,7 @@ max edge count.
Modifies `superface`. Adds the results to the end of `output`. Modifies `superface`. Adds the results to the end of `output`.
================== ==================
*/ */
inline void SplitFaceIntoFragments(std::vector<size_t> &superface, std::vector<std::vector<size_t>> &output) inline void SplitFaceIntoFragments(std::vector<size_t> &superface, std::list<std::vector<size_t>> &output)
{ {
size_t base = 0; size_t base = 0;
size_t remaining = superface.size(); size_t remaining = superface.size();
@ -187,11 +187,11 @@ inline void SplitFaceIntoFragments(std::vector<size_t> &superface, std::vector<s
} }
// copy the remainder back to the input face // copy the remainder back to the input face
superface.resize(remaining);
for (size_t i = 0; i < remaining; i++) { for (size_t i = 0; i < remaining; i++) {
superface[i] = superface[(i + base) % superface.size()]; superface[i] = superface[(i + base) % superface.size()];
} }
superface.resize(remaining);
} }
float AngleOfTriangle(const qvec3d &a, const qvec3d &b, const qvec3d &c) float AngleOfTriangle(const qvec3d &a, const qvec3d &b, const qvec3d &c)
@ -225,7 +225,7 @@ Generate a superface (the input face `f` but with all of the
verts in the world added that lay on the line) and return it verts in the world added that lay on the line) and return it
================== ==================
*/ */
static std::vector<size_t> CreateSuperFace(node_t *headnode, node_t *node, face_t *f) static std::vector<size_t> CreateSuperFace(node_t *headnode, face_t *f)
{ {
std::vector<size_t> superface; std::vector<size_t> superface;
@ -245,7 +245,7 @@ static std::vector<size_t> CreateSuperFace(node_t *headnode, node_t *node, face_
qvec3d e2 = map.bsp.dvertexes[v2]; qvec3d e2 = map.bsp.dvertexes[v2];
edge_verts.clear(); edge_verts.clear();
FindEdgeVerts_FaceBounds(headnode, node, edge_start, e2, edge_verts); FindEdgeVerts_FaceBounds(headnode, edge_start, e2, edge_verts);
vec_t len; vec_t len;
qvec3d edge_dir = qv::normalize(e2 - edge_start, len); qvec3d edge_dir = qv::normalize(e2 - edge_start, len);
@ -265,9 +265,9 @@ It's still a convex face with wound vertices, though, so we
can split it into several triangle fans. can split it into several triangle fans.
================== ==================
*/ */
static std::vector<std::vector<size_t>> RetopologizeFace(const std::vector<size_t> &vertices) static std::list<std::vector<size_t>> RetopologizeFace(const std::vector<size_t> &vertices)
{ {
std::vector<std::vector<size_t>> result; std::list<std::vector<size_t>> result;
// the copy we're working on // the copy we're working on
std::vector<size_t> input(vertices); std::vector<size_t> input(vertices);
@ -421,9 +421,9 @@ FixFaceEdges
If the face has any T-junctions, fix them here. If the face has any T-junctions, fix them here.
================== ==================
*/ */
static void FixFaceEdges(node_t *headnode, node_t *node, face_t *f) static void FixFaceEdges(node_t *headnode, face_t *f)
{ {
std::vector<size_t> superface = CreateSuperFace(headnode, node, f); std::vector<size_t> superface = CreateSuperFace(headnode, f);
if (superface.size() < 3) { if (superface.size() < 3) {
// entire face collapsed // entire face collapsed
@ -460,7 +460,7 @@ static void FixFaceEdges(node_t *headnode, node_t *node, face_t *f)
} }
// temporary storage for result faces // temporary storage for result faces
std::vector<std::vector<size_t>> faces; std::list<std::vector<size_t>> faces;
if (i == superface.size()) { if (i == superface.size()) {
// can't simply rotate to eliminate zero-area triangles, so we have // can't simply rotate to eliminate zero-area triangles, so we have
@ -492,27 +492,28 @@ static void FixFaceEdges(node_t *headnode, node_t *node, face_t *f)
Q_assert(faces.size()); Q_assert(faces.size());
// split giant superfaces into subfaces // split giant superfaces into subfaces
size_t superface_count = faces.size(); for (auto &face : faces) {
SplitFaceIntoFragments(face, faces);
for (size_t i = 0; i < superface_count; i++) {
SplitFaceIntoFragments(faces[i], faces);
} }
// move the results into the face // move the results into the face
f->output_vertices = std::move(faces[0]); f->output_vertices = std::move(faces.front());
f->fragments.resize(faces.size() - 1); f->fragments.resize(faces.size() - 1);
for (size_t i = 1; i < faces.size(); i++) { i = 0;
f->fragments[i - 1].output_vertices = std::move(faces[i]); for (auto it = ++faces.begin(); it != faces.end(); it++, i++) {
f->fragments[i].output_vertices = std::move(*it);
} }
} }
#include <common/parallel.hh>
/* /*
================== ==================
FixEdges_r FixEdges_r
================== ==================
*/ */
static void FixEdges_r(node_t *headnode, node_t *node) static void FindFaces_r(node_t *node, std::unordered_set<face_t *> &faces)
{ {
if (node->planenum == PLANENUM_LEAF) { if (node->planenum == PLANENUM_LEAF) {
return; return;
@ -521,12 +522,12 @@ static void FixEdges_r(node_t *headnode, node_t *node)
for (auto &f : node->facelist) { for (auto &f : node->facelist) {
// might have been omitted earlier, so `output_vertices` will be empty // might have been omitted earlier, so `output_vertices` will be empty
if (f->output_vertices.size()) { if (f->output_vertices.size()) {
FixFaceEdges(headnode, node, f.get()); faces.insert(f.get());
} }
} }
FixEdges_r(headnode, node->children[0].get()); FindFaces_r(node->children[0].get(), faces);
FixEdges_r(headnode, node->children[1].get()); FindFaces_r(node->children[1].get(), faces);
} }
/* /*
@ -548,7 +549,13 @@ void TJunc(node_t *headnode)
c_retopology = 0; c_retopology = 0;
c_faceretopology = 0; c_faceretopology = 0;
FixEdges_r (headnode, headnode); std::unordered_set<face_t *> faces;
FindFaces_r(headnode, faces);
logging::parallel_for_each(faces, [&](auto &face) {
FixFaceEdges(headnode, face);
});
logging::print (logging::flag::STAT, "{:5} edges degenerated\n", c_degenerate); logging::print (logging::flag::STAT, "{:5} edges degenerated\n", c_degenerate);
logging::print (logging::flag::STAT, "{:5} faces degenerated\n", c_facecollapse); logging::print (logging::flag::STAT, "{:5} faces degenerated\n", c_facecollapse);

View File

@ -177,15 +177,16 @@ static void ExportLeaf(node_t *node)
continue; continue;
// FIXME: this can happen when compiling some Q2 maps // FIXME: this can happen when compiling some Q2 maps
// as Q1. // as Q1.
if (!face->outputnumber.has_value()) if (face->outputnumber.has_value()) {
continue; /* emit a marksurface */
map.bsp.dleaffaces.push_back(face->outputnumber.value());
/* emit a marksurface */ }
map.bsp.dleaffaces.push_back(face->outputnumber.value());
/* grab tjunction split faces */ /* grab tjunction split faces */
for (auto &fragment : face->fragments) { for (auto &fragment : face->fragments) {
map.bsp.dleaffaces.push_back(fragment.outputnumber.value()); if (fragment.outputnumber.has_value()) {
map.bsp.dleaffaces.push_back(fragment.outputnumber.value());
}
} }
} }
dleaf.nummarksurfaces = static_cast<int>(map.bsp.dleaffaces.size()) - dleaf.firstmarksurface; dleaf.nummarksurfaces = static_cast<int>(map.bsp.dleaffaces.size()) - dleaf.firstmarksurface;