diff --git a/include/qbsp/qbsp.hh b/include/qbsp/qbsp.hh index 7935e1b4..47d4eee8 100644 --- a/include/qbsp/qbsp.hh +++ b/include/qbsp/qbsp.hh @@ -253,6 +253,7 @@ public: setting_enum filltype{this, "filltype", filltype_t::AUTO, { { "auto", filltype_t::AUTO }, { "inside", filltype_t::INSIDE }, { "outside", filltype_t::OUTSIDE } }, &common_format_group, "whether to fill the map from the outside in (lenient), from the inside out (aggressive), or to automatically decide based on the hull being used."}; setting_invertible_bool allow_upgrade{this, "allowupgrade", true, &common_format_group, "allow formats to \"upgrade\" to compatible extended formats when a limit is exceeded (ie Quake BSP to BSP2)"}; + setting_int32 maxedges{this, "maxedges", 64, &map_development_group, "the max number of edges/vertices on a single face before it is split into another face"}; void setParameters(int argc, const char **argv) override { diff --git a/include/qbsp/winding.hh b/include/qbsp/winding.hh index 45c5043f..bf02c0f6 100644 --- a/include/qbsp/winding.hh +++ b/include/qbsp/winding.hh @@ -23,8 +23,10 @@ #include "common/polylib.hh" -constexpr size_t MAXEDGES = 64; +// now much a winding will use on the stack +// before needing an overflow. +constexpr size_t STACK_WINDING_SIZE = 64; -using winding_t = polylib::winding_base_t; +using winding_t = polylib::winding_base_t; winding_t BaseWindingForPlane(const qplane3d &p); diff --git a/qbsp/brush.cc b/qbsp/brush.cc index 1e9aa89e..e628b074 100644 --- a/qbsp/brush.cc +++ b/qbsp/brush.cc @@ -434,10 +434,6 @@ static std::vector CreateBrushFaces(const mapentity_t *src, hullbrush_t continue; // overconstrained plane } - if (w->size() > MAXEDGES) { - FError("face->numpoints > MAXEDGES ({}), source face on line {}", MAXEDGES, mapface.linenum); - } - // this face is a keeper side_t &f = facelist.emplace_back(); f.planenum = PLANENUM_LEAF; diff --git a/qbsp/csg.cc b/qbsp/csg.cc index 19b71ac8..286132c4 100644 --- a/qbsp/csg.cc +++ b/qbsp/csg.cc @@ -118,9 +118,6 @@ std::tuple, std::unique_ptr> SplitFace(std::uniq auto new_back = NewFaceFromFace(in.get()); new_back->w = std::move(back_winding.value()); - if (new_front->w.size() > MAXEDGES || new_back->w.size() > MAXEDGES) - FError("Internal error: numpoints > MAXEDGES"); - return {std::move(new_front), std::move(new_back)}; } diff --git a/qbsp/faces.cc b/qbsp/faces.cc index 523d9b72..e4380e96 100644 --- a/qbsp/faces.cc +++ b/qbsp/faces.cc @@ -140,8 +140,8 @@ static void FindFaceFragmentEdges(face_t *face, face_fragment_t *fragment) { Q_assert(fragment->outputnumber == std::nullopt); - if (fragment->output_vertices.size() > MAXEDGES) { - FError("Internal error: face->numpoints > MAXEDGES"); + if (qbsp_options.maxedges.value() && fragment->output_vertices.size() > qbsp_options.maxedges.value()) { + FError("Internal error: face->numpoints > max edges ({})", qbsp_options.maxedges.value()); } fragment->edges.resize(fragment->output_vertices.size()); diff --git a/qbsp/merge.cc b/qbsp/merge.cc index ad518cbc..8501139f 100644 --- a/qbsp/merge.cc +++ b/qbsp/merge.cc @@ -127,11 +127,6 @@ static std::unique_ptr TryMerge(const face_t *f1, const face_t *f2) keep2 = dot < -CONTINUOUS_EPSILON; // build the new polygon - if (f1->w.size() + f2->w.size() > MAXEDGES) { - logging::funcprint("WARNING: Too many edges\n"); - return NULL; - } - std::unique_ptr newf = NewFaceFromFace(f1); // copy first polygon diff --git a/qbsp/qbsp.cc b/qbsp/qbsp.cc index 94f0252d..043309b6 100644 --- a/qbsp/qbsp.cc +++ b/qbsp/qbsp.cc @@ -245,6 +245,13 @@ void qbsp_settings::postinitialize(int argc, const char **argv) } } + // side effects from Quake II + if (qbsp_options.target_game->id == GAME_QUAKE_II) { + if (!maxedges.isChanged()) { + maxedges.setValueLocked(0); + } + } + // load texture defs for (auto &def : texturedefs.values()) { load_texture_def(def); diff --git a/qbsp/tjunc.cc b/qbsp/tjunc.cc index cfbfb748..4ed8eb79 100644 --- a/qbsp/tjunc.cc +++ b/qbsp/tjunc.cc @@ -164,20 +164,22 @@ Modifies `superface`. Adds the results to the end of `output`. */ inline void SplitFaceIntoFragments(std::vector &superface, std::list> &output) { + const int32_t &maxedges = qbsp_options.maxedges.value(); + // split into multiple fragments, because of vertex overload - while (superface.size() > MAXEDGES) { + while (superface.size() > maxedges) { c_faceoverflows++; // copy MAXEDGES from our current face - std::vector &newf = output.emplace_back(MAXEDGES); - std::copy_n(superface.begin(), MAXEDGES, newf.begin()); + std::vector &newf = output.emplace_back(maxedges); + std::copy_n(superface.begin(), maxedges, newf.begin()); // remove everything in-between from the superface // except for the last edge we just wrote (0 and MAXEDGES-1) - std::copy(superface.begin() + MAXEDGES - 1, superface.end(), superface.begin() + 1); + std::copy(superface.begin() + maxedges - 1, superface.end(), superface.begin() + 1); // resize superface; we need enough room to store the two extra verts - superface.resize(superface.size() - MAXEDGES + 2); + superface.resize(superface.size() - maxedges + 2); } // move the first face to the end, since that's logically where it belongs now @@ -482,8 +484,10 @@ static void FixFaceEdges(node_t *headnode, face_t *f) Q_assert(faces.size()); // split giant superfaces into subfaces - for (auto &face : faces) { - SplitFaceIntoFragments(face, faces); + if (qbsp_options.maxedges.value()) { + for (auto &face : faces) { + SplitFaceIntoFragments(face, faces); + } } // move the results into the face