From 893b0b080e43fa96c7b09adc4beb9458c97b15f7 Mon Sep 17 00:00:00 2001 From: Eric Wasylishen Date: Tue, 3 May 2022 02:33:24 -0600 Subject: [PATCH] qbsp: move to unique_ptr vectors --- include/qbsp/brush.hh | 1 + include/qbsp/csg4.hh | 5 +- include/qbsp/map.hh | 9 ++-- include/qbsp/outside.hh | 5 ++ include/qbsp/solidbsp.hh | 6 +-- qbsp/brush.cc | 2 +- qbsp/csg4.cc | 98 ++++++++++++++++++++++------------------ qbsp/map.cc | 12 +++-- qbsp/outside.cc | 4 +- qbsp/portals.cc | 1 + qbsp/qbsp.cc | 32 +++++++------ qbsp/solidbsp.cc | 93 +++++++++++++++++++------------------- qbsp/surfaces.cc | 4 +- qbsp/test_qbsp.cc | 1 + 14 files changed, 153 insertions(+), 120 deletions(-) diff --git a/include/qbsp/brush.hh b/include/qbsp/brush.hh index 7157cab8..9d6836fb 100644 --- a/include/qbsp/brush.hh +++ b/include/qbsp/brush.hh @@ -33,6 +33,7 @@ struct brush_t * the BrushBSP will have this pointing back to the original brush in mapentity_t::brushes. */ brush_t *original; + uint32_t file_order; aabb3d bounds; std::vector faces; contentflags_t contents; /* BSP contents */ diff --git a/include/qbsp/csg4.hh b/include/qbsp/csg4.hh index d1d3d3cd..1bd97e25 100644 --- a/include/qbsp/csg4.hh +++ b/include/qbsp/csg4.hh @@ -21,14 +21,13 @@ #pragma once -#include - #include #include #include #include +struct brush_t; struct face_t; int MakeSkipTexinfo(); @@ -38,4 +37,4 @@ face_t *MirrorFace(const face_t *face); std::tuple SplitFace(face_t *in, const qplane3d &split); void UpdateFaceSphere(face_t *in); bool BrushGE(const brush_t &a, const brush_t &b); -std::vector ChopBrushes(const std::vector &input); +std::vector> ChopBrushes(const std::vector> &input); diff --git a/include/qbsp/map.hh b/include/qbsp/map.hh index f4dc9421..e7f7fa5b 100644 --- a/include/qbsp/map.hh +++ b/include/qbsp/map.hh @@ -21,8 +21,9 @@ #pragma once -#include +#include +#include #include #include "common/cmdlib.hh" @@ -32,6 +33,8 @@ #include #include +struct brush_t; + struct qbsp_plane_t : qplane3d { int type = 0; @@ -96,7 +99,7 @@ public: std::vector> epairs; aabb3d bounds; - std::vector brushes; + std::vector> brushes; int firstoutputfacenumber = -1; std::optional outputmodelnumber = std::nullopt; @@ -241,6 +244,6 @@ void ExportObj_Brushes(const std::string &filesuffix, const std::vector &list); +void WriteBspBrushMap(const fs::path &name, const std::vector> &list); bool IsValidTextureProjection(const qvec3f &faceNormal, const qvec3f &s_vec, const qvec3f &t_vec); diff --git a/include/qbsp/outside.hh b/include/qbsp/outside.hh index 030d546e..dc9f56d9 100644 --- a/include/qbsp/outside.hh +++ b/include/qbsp/outside.hh @@ -21,6 +21,11 @@ #pragma once +#include + +class mapentity_t; +struct node_t; + bool FillOutside(mapentity_t *entity, node_t *node, const int hullnum); std::vector FindOccupiedClusters(node_t *headnode); diff --git a/include/qbsp/solidbsp.hh b/include/qbsp/solidbsp.hh index e47a2af5..c60b4182 100644 --- a/include/qbsp/solidbsp.hh +++ b/include/qbsp/solidbsp.hh @@ -21,21 +21,21 @@ #pragma once -#include - #include #include #include #include +#include extern std::atomic splitnodes; +struct brush_t; struct node_t; struct face_t; class mapentity_t; void DetailToSolid(node_t *node); void PruneNodes(node_t *node); -twosided> SplitBrush(const brush_t &brush, const qplane3d &split); +twosided> SplitBrush(std::unique_ptr brush, const qplane3d &split); node_t *SolidBSP(mapentity_t *entity, bool midsplit); diff --git a/qbsp/brush.cc b/qbsp/brush.cc index 4f12d925..bbb94bb2 100644 --- a/qbsp/brush.cc +++ b/qbsp/brush.cc @@ -965,7 +965,7 @@ static void Brush_LoadEntity(mapentity_t *dst, const mapentity_t *src, const int } else { stats.liquid++; } - dst->brushes.push_back(std::move(brush.value())); + dst->brushes.push_back(std::make_unique(brush.value())); dst->bounds += brush->bounds; } diff --git a/qbsp/csg4.cc b/qbsp/csg4.cc index b22e6606..7cd29ff8 100644 --- a/qbsp/csg4.cc +++ b/qbsp/csg4.cc @@ -215,21 +215,11 @@ static void FreeFaces(std::list &facelist) //========================================================================== -/* -================== -BrushIndexInMap - -Returns the index of the brush in the .map files. -Only call with an "original" brush (from entity->brushes). -Used for clipping priority. -================== -*/ -static int BrushIndexInMap(const mapentity_t *entity, const brush_t *brush) +static std::vector> SingleBrush(std::unique_ptr a) { - Q_assert(brush >= entity->brushes.data()); - Q_assert(brush < (entity->brushes.data() + entity->brushes.size())); - - return static_cast(brush - entity->brushes.data()); + std::vector> res; + res.push_back(std::move(a)); + return res; } /* @@ -239,30 +229,46 @@ SubtractBrush Returns the fragments from a - b ================== */ -std::vector SubtractBrush(const brush_t& a, const brush_t& b) +static std::vector> SubtractBrush(std::unique_ptr a, const brush_t& b) { // first, check if `a` is fully in front of _any_ of b's planes for (const auto &side : b.faces) { - auto [front, back] = SplitBrush(a, Face_Plane(&side)); - if (front && !back) { + // is `a` fully in front of `side`? + bool fully_infront = true; + + // fixme-brushbsp: factor this out somewhere + for (const auto &a_face : a->faces) { + for (const auto &a_point : a_face.w) { + if (Face_Plane(&side).distance_to(a_point) < 0) { + fully_infront = false; + break; + } + } + if (!fully_infront) { + break; + } + } + + if (fully_infront) { // `a` is fully in front of this side of b, so they don't actually intersect - return {a}; + return SingleBrush(std::move(a)); } } - std::vector frontlist; - std::vector unclassified{a}; + std::vector> frontlist; + std::vector> unclassified = SingleBrush(std::move(a)); for (const auto &side : b.faces) { - std::vector new_unclassified; + std::vector> new_unclassified; - for (const auto &fragment : unclassified) { - auto [front, back] = SplitBrush(fragment, Face_Plane(&side)); + for (auto &fragment : unclassified) { + // destructively processing `unclassified` here + auto [front, back] = SplitBrush(std::move(fragment), Face_Plane(&side)); if (front) { - frontlist.push_back(*front); + frontlist.push_back(std::move(front)); } if (back) { - new_unclassified.push_back(*back); + new_unclassified.push_back(std::move(back)); } } @@ -284,7 +290,7 @@ bool BrushGE(const brush_t& a, const brush_t& b) // same contents clip each other if (a.contents == b.contents && a.contents.clips_same_type()) { // map file order - return &a > &b; + return a.file_order > b.file_order; } // only chop if at least one of the two contents is @@ -298,7 +304,7 @@ bool BrushGE(const brush_t& a, const brush_t& b) if (a_pri == b_pri) { // map file order - return &a > &b; + return a.file_order > b.file_order; } return a_pri >= b_pri; @@ -311,12 +317,13 @@ ChopBrushes Clips off any overlapping portions of brushes ================== */ -std::vector ChopBrushes(const std::vector& input) +std::vector> ChopBrushes(const std::vector>& input) { logging::print(logging::flag::PROGRESS, "---- {} ----\n", __func__); - // output vector for the parallel_for - std::vector> brush_fragments; + // each inner vector corresponds to a brush in `input` + // (set up this way for thread safety) + std::vector>> brush_fragments; brush_fragments.resize(input.size()); /* @@ -327,26 +334,31 @@ std::vector ChopBrushes(const std::vector& input) * * The output of this is a face list for each brush called "outside" */ - tbb::parallel_for(static_cast(0), input.size(), [input, &brush_fragments](const size_t i) { - const auto &brush = input[i]; + tbb::parallel_for(static_cast(0), input.size(), [&](const size_t i) { + const auto& brush = input[i]; + // the fragments `brush` is chopped into - std::vector brush_result{brush}; + std::vector> brush_result = SingleBrush( + // start with a copy of brush + std::make_unique(*brush) + ); for (auto &clipbrush : input) { - if (&brush == &clipbrush) { + if (brush == clipbrush) { continue; } - if (brush.bounds.disjoint(clipbrush.bounds)) { + if (brush->bounds.disjoint(clipbrush->bounds)) { continue; } - if (BrushGE(clipbrush, brush)) { - std::vector new_result; + if (BrushGE(*clipbrush, *brush)) { + std::vector> new_result; - // clipbrush is stronger. clip all existing fragments to clipbrush - for (const auto ¤t_fragment : brush_result) { - for (const auto &new_fragment : SubtractBrush(current_fragment, clipbrush)) { - new_result.push_back(new_fragment); + // clipbrush is stronger. + // rebuild existing fragments in brush_result, cliping them to clipbrush + for (auto ¤t_fragment : brush_result) { + for (auto &new_fragment : SubtractBrush(std::move(current_fragment), *clipbrush)) { + new_result.push_back(std::move(new_fragment)); } } @@ -355,11 +367,11 @@ std::vector ChopBrushes(const std::vector& input) } // save the result - brush_fragments[i] = brush_result; + brush_fragments[i] = std::move(brush_result); }); // Non parallel part: - std::vector result; + std::vector> result; for (auto &fragment_list : brush_fragments) { for (auto &fragment : fragment_list) { result.push_back(std::move(fragment)); diff --git a/qbsp/map.cc b/qbsp/map.cc index 9e4b43ea..6be9e042 100644 --- a/qbsp/map.cc +++ b/qbsp/map.cc @@ -2271,7 +2271,7 @@ WriteBspBrushMap from q3map ================== */ -void WriteBspBrushMap(const fs::path &name, const std::vector &list) +void WriteBspBrushMap(const fs::path &name, const std::vector> &list) { logging::print("writing {}\n", name); std::ofstream f(name); @@ -2283,7 +2283,7 @@ void WriteBspBrushMap(const fs::path &name, const std::vector &list) for (auto &brush : list) { fmt::print(f, "{{\n"); - for (auto &face : brush.faces) { + for (auto &face : brush->faces) { // FIXME: Factor out this mess qbsp_plane_t plane = map.planes.at(face.planenum); @@ -2322,15 +2322,17 @@ from q3map */ static void TestExpandBrushes(const mapentity_t *src) { - std::vector hull1brushes; + std::vector> hull1brushes; for (int i = 0; i < src->nummapbrushes; i++) { const mapbrush_t *mapbrush = &src->mapbrush(i); std::optional hull1brush = LoadBrush( src, mapbrush, {CONTENTS_SOLID}, {}, rotation_t::none, options.target_game->id == GAME_QUAKE_II ? -1 : 1); - if (hull1brush) - hull1brushes.emplace_back(std::move(hull1brush.value())); + if (hull1brush) { + hull1brushes.emplace_back( + std::make_unique(std::move(*hull1brush))); + } } WriteBspBrushMap("expanded.map", hull1brushes); diff --git a/qbsp/outside.cc b/qbsp/outside.cc index 750c552d..fa5b5a68 100644 --- a/qbsp/outside.cc +++ b/qbsp/outside.cc @@ -19,6 +19,8 @@ See file, 'COPYING', for details. */ +#include +#include #include #include #include @@ -357,7 +359,7 @@ std::vector FindOccupiedClusters(node_t *headnode) static void MarkBrushSidesInvisible(mapentity_t *entity) { for (auto &brush : entity->brushes) { - for (auto &face : brush.faces) { + for (auto &face : brush->faces) { face.visible = false; } } diff --git a/qbsp/portals.cc b/qbsp/portals.cc index 5bfdfd43..920e146c 100644 --- a/qbsp/portals.cc +++ b/qbsp/portals.cc @@ -20,6 +20,7 @@ */ // portals.c +#include #include #include #include diff --git a/qbsp/qbsp.cc b/qbsp/qbsp.cc index 25958e64..dd7c8673 100644 --- a/qbsp/qbsp.cc +++ b/qbsp/qbsp.cc @@ -349,12 +349,12 @@ static void ExportBrushList(mapentity_t *entity, node_t *node) brush_state = {}; for (auto &b : entity->brushes) { - b.outputnumber = { static_cast(map.bsp.dbrushes.size()) }; + b->outputnumber = {static_cast(map.bsp.dbrushes.size())}; dbrush_t &brush = map.bsp.dbrushes.emplace_back( - dbrush_t{static_cast(map.bsp.dbrushsides.size()), 0, b.contents.native}); + dbrush_t{static_cast(map.bsp.dbrushsides.size()), 0, b->contents.native}); - auto bevels = AddBrushBevels(b); + auto bevels = AddBrushBevels(*b); for (auto &plane : bevels) { map.bsp.dbrushsides.push_back( @@ -654,6 +654,11 @@ static void ProcessEntity(mapentity_t *entity, const int hullnum) logging::print(logging::flag::PROGRESS, "---- Brush_LoadEntity ----\n"); auto stats = Brush_LoadEntity(entity, hullnum); + // assign brush file order + for (size_t i = 0; i < entity->brushes.size(); ++i) { + entity->brushes[i]->file_order = i; + } + entity->brushes = ChopBrushes(entity->brushes); if (entity == map.world_entity() && hullnum == 0) { @@ -880,13 +885,14 @@ Generates a submodel's direct brush information to a separate file, so the engin hull sizes */ -static void BSPX_Brushes_AddModel(struct bspxbrushes_s *ctx, int modelnum, std::vector &brushes) +static void BSPX_Brushes_AddModel( + struct bspxbrushes_s *ctx, int modelnum, std::vector> &brushes) { bspxbrushes_permodel permodel{1, modelnum}; for (auto &b : brushes) { permodel.numbrushes++; - for (auto &f : b.faces) { + for (auto &f : b->faces) { /*skip axial*/ if (fabs(map.planes[f.planenum].normal[0]) == 1 || fabs(map.planes[f.planenum].normal[1]) == 1 || fabs(map.planes[f.planenum].normal[2]) == 1) @@ -907,7 +913,7 @@ static void BSPX_Brushes_AddModel(struct bspxbrushes_s *ctx, int modelnum, std:: for (auto &b : brushes) { bspxbrushes_perbrush perbrush{}; - for (auto &f : b.faces) { + for (auto &f : b->faces) { /*skip axial*/ if (fabs(map.planes[f.planenum].normal[0]) == 1 || fabs(map.planes[f.planenum].normal[1]) == 1 || fabs(map.planes[f.planenum].normal[2]) == 1) @@ -915,9 +921,9 @@ static void BSPX_Brushes_AddModel(struct bspxbrushes_s *ctx, int modelnum, std:: perbrush.numfaces++; } - perbrush.bounds = b.bounds; + perbrush.bounds = b->bounds; - switch (b.contents.native) { + switch (b->contents.native) { // contents should match the engine. case CONTENTS_EMPTY: // really an error, but whatever case CONTENTS_SOLID: // these are okay @@ -925,21 +931,21 @@ static void BSPX_Brushes_AddModel(struct bspxbrushes_s *ctx, int modelnum, std:: case CONTENTS_SLIME: case CONTENTS_LAVA: case CONTENTS_SKY: - if (b.contents.is_clip()) { + if (b->contents.is_clip()) { perbrush.contents = -8; } else { - perbrush.contents = b.contents.native; + perbrush.contents = b->contents.native; } break; // case CONTENTS_LADDER: // perbrush.contents = -16; // break; default: { - if (b.contents.is_clip()) { + if (b->contents.is_clip()) { perbrush.contents = -8; } else { logging::print("WARNING: Unknown contents: {}. Translating to solid.\n", - b.contents.to_string(options.target_game)); + b->contents.to_string(options.target_game)); perbrush.contents = CONTENTS_SOLID; } break; @@ -948,7 +954,7 @@ static void BSPX_Brushes_AddModel(struct bspxbrushes_s *ctx, int modelnum, std:: str <= perbrush; - for (auto &f : b.faces) { + for (auto &f : b->faces) { /*skip axial*/ if (fabs(map.planes[f.planenum].normal[0]) == 1 || fabs(map.planes[f.planenum].normal[1]) == 1 || fabs(map.planes[f.planenum].normal[2]) == 1) diff --git a/qbsp/solidbsp.cc b/qbsp/solidbsp.cc index 2ca2515f..7777c83b 100644 --- a/qbsp/solidbsp.cc +++ b/qbsp/solidbsp.cc @@ -297,7 +297,7 @@ ChooseMidPlaneFromList The clipping hull BSP doesn't worry about avoiding splits ================== */ -static face_t *ChooseMidPlaneFromList(std::vector &brushes, const aabb3d &bounds) +static face_t *ChooseMidPlaneFromList(std::vector> &brushes, const aabb3d &bounds) { /* pick the plane that splits the least */ vec_t bestaxialmetric = VECT_MAX; @@ -307,11 +307,11 @@ static face_t *ChooseMidPlaneFromList(std::vector &brushes, const aabb3 for (int pass = 0; pass < 2; pass++) { for (auto &brush : brushes) { - if (brush.contents.is_detail() != (pass == 1)) { + if (brush->contents.is_detail() != (pass == 1)) { continue; } - for (auto &face : brush.faces) { + for (auto &face : brush->faces) { if (face.onnode) continue; if (!face.visible) { @@ -363,7 +363,7 @@ The real BSP heuristic fixme-brushbsp: prefer splits that include a lot of brush sides? ================== */ -static face_t *ChoosePlaneFromList(std::vector &brushes, const aabb3d &bounds) +static face_t *ChoosePlaneFromList(std::vector> &brushes, const aabb3d &bounds) { /* pick the plane that splits the least */ int minsplits = INT_MAX - 1; @@ -373,11 +373,11 @@ static face_t *ChoosePlaneFromList(std::vector &brushes, const aabb3d & /* Two passes - exhaust all non-detail faces before details */ for (int pass = 0; pass < 2; pass++) { for (auto &brush : brushes) { - if (brush.contents.is_detail() != (pass == 1)) { + if (brush->contents.is_detail() != (pass == 1)) { continue; } - for (auto &face : brush.faces) { + for (auto &face : brush->faces) { if (face.onnode) { continue; } @@ -394,7 +394,7 @@ static face_t *ChoosePlaneFromList(std::vector &brushes, const aabb3d & // now check all of the other faces in `brushes` and count how many // would get split if we used `face` as the splitting plane for (auto &brush2 : brushes) { - for (auto &face2 : brush2.faces) { + for (auto &face2 : brush2->faces) { if (face2.planenum == face.planenum || face2.onnode) continue; if (!face2.visible) @@ -458,13 +458,13 @@ returns NULL if the surface list can not be divided any more (a leaf) Called in parallel. ================== */ -static face_t *SelectPartition(std::vector &brushes) +static face_t *SelectPartition(std::vector> &brushes) { // calculate a bounding box of the entire surfaceset aabb3d bounds; for (auto &brush : brushes) { - bounds += brush.bounds; + bounds += brush->bounds; } // how much of the map are we partitioning? @@ -610,20 +610,20 @@ vec_t BrushVolume(const brush_t &brush) ================ SplitBrush -Generates two new brushes, leaving the original -unchanged +Note, it's useful to take/return std::unique_ptr so it can quickly return the +input. https://github.com/id-Software/Quake-2-Tools/blob/master/bsp/qbsp3/brushbsp.c#L935 ================ */ -twosided> SplitBrush(const brush_t &brush, const qplane3d &split) +twosided> SplitBrush(std::unique_ptr brush, const qplane3d &split) { - twosided> result; + twosided> result; // check all points vec_t d_front = 0; vec_t d_back = 0; - for (auto &face : brush.faces) { + for (auto &face : brush->faces) { for (int j = 0; j < face.w.size(); j++) { vec_t d = qv::dot(face.w[j], split.normal) - split.dist; if (d > 0 && d > d_front) @@ -634,18 +634,18 @@ twosided> SplitBrush(const brush_t &brush, const qplane3d } if (d_front < 0.1) // PLANESIDE_EPSILON) { // only on back - result.back = {brush}; + result.back = std::move(brush); return result; } if (d_back > -0.1) // PLANESIDE_EPSILON) { // only on front - result.front = {brush}; + result.front = std::move(brush); return result; } // create a new winding from the split plane auto w = std::optional{BaseWindingForPlane(split)}; - for (auto &face : brush.faces) { + for (auto &face : brush->faces) { if (!w) { break; } @@ -654,11 +654,11 @@ twosided> SplitBrush(const brush_t &brush, const qplane3d } if (!w || WindingIsTiny(*w)) { // the brush isn't really split - side_t side = BrushMostlyOnSide(brush, split); + side_t side = BrushMostlyOnSide(*brush, split); if (side == SIDE_FRONT) - result.front = {brush}; + result.front = std::move(brush); else - result.back = {brush}; + result.back = std::move(brush); return result; } @@ -673,16 +673,16 @@ twosided> SplitBrush(const brush_t &brush, const qplane3d // start with 2 empty brushes for (int i = 0; i < 2; i++) { - result[i] = { brush_t{} }; - result[i]->original = brush.original; + result[i] = std::make_unique(); + result[i]->original = brush->original; // fixme-brushbsp: add a brush_t copy constructor to make sure we get all fields - result[i]->contents = brush.contents; - result[i]->lmshift = brush.lmshift; + result[i]->contents = brush->contents; + result[i]->lmshift = brush->lmshift; } // split all the current windings - for (const auto& face : brush.faces) { + for (const auto &face : brush->faces) { auto cw = face.w.clip(split, 0 /*PLANESIDE_EPSILON*/); for (size_t j = 0; j < 2; j++) { if (!cw[j]) @@ -721,7 +721,7 @@ twosided> SplitBrush(const brush_t &brush, const qplane3d } if (result[i]->faces.size() < 3 || bogus) { - result[i] = std::nullopt; + result[i] = nullptr; } } @@ -731,10 +731,10 @@ twosided> SplitBrush(const brush_t &brush, const qplane3d else logging::print("split not on both sides\n"); if (result[0]) { - result.front = {brush}; + result.front = std::move(brush); } if (result[1]) { - result.back = {brush}; + result.back = std::move(brush); } return result; } @@ -764,7 +764,7 @@ twosided> SplitBrush(const brush_t &brush, const qplane3d for (i = 0; i < 2; i++) { v1 = BrushVolume(*result[i]); if (v1 < 1.0) { - result[i] = std::nullopt; + result[i] = nullptr; // qprintf ("tiny volume after clip\n"); } } @@ -785,10 +785,10 @@ inline void DivideNodeBounds(node_t *node, const qbsp_plane_t &split) DivideBounds(node->bounds, split, node->children[0]->bounds, node->children[1]->bounds); } -static bool AllDetail(const std::vector &brushes) +static bool AllDetail(const std::vector> &brushes) { for (auto &brush : brushes) { - if (!brush.contents.is_detail()) { + if (!brush->contents.is_detail()) { return false; } } @@ -818,18 +818,18 @@ original faces that have some fragment inside this leaf. Called in parallel. ================== */ -static void CreateLeaf(const std::vector &brushes, node_t *leafnode) +static void CreateLeaf(std::vector> brushes, node_t *leafnode) { leafnode->facelist.clear(); leafnode->planenum = PLANENUM_LEAF; leafnode->contents = options.target_game->create_empty_contents(); for (auto &brush : brushes) { - leafnode->contents = MergeContents(leafnode->contents, brush.contents); + leafnode->contents = MergeContents(leafnode->contents, brush->contents); } for (auto &brush : brushes) { - Q_assert(brush.original != nullptr); - leafnode->original_brushes.push_back(brush.original); + Q_assert(brush->original != nullptr); + leafnode->original_brushes.push_back(brush->original); } if (leafnode->contents.extended & CFLAGS_ILLUSIONARY_VISBLOCKER) { @@ -859,14 +859,14 @@ PartitionBrushes Called in parallel. ================== */ -static void PartitionBrushes(std::vector brushes, node_t *node) +static void PartitionBrushes(std::vector> brushes, node_t *node) { face_t *split = SelectPartition(brushes); if (split == nullptr) { // this is a leaf node node->planenum = PLANENUM_LEAF; - CreateLeaf(brushes, node); + CreateLeaf(std::move(brushes), node); return; } @@ -882,10 +882,11 @@ static void PartitionBrushes(std::vector brushes, node_t *node) DivideNodeBounds(node, splitplane); // multiple surfaces, so split all the polysurfaces into front and back lists - std::vector frontlist, backlist; + std::vector> frontlist, backlist; for (auto &brush : brushes) { - auto frags = SplitBrush(brush, splitplane); + // NOTE: we're destroying `brushes` here with the std::move() + auto frags = SplitBrush(std::move(brush), splitplane); // mark faces which were used as a splitter for (auto &brushMaybe : frags) { @@ -902,13 +903,13 @@ static void PartitionBrushes(std::vector brushes, node_t *node) if (frags.front->faces.empty()) { FError("Surface with no faces"); } - frontlist.emplace_back(std::move(*frags.front)); + frontlist.emplace_back(std::move(frags.front)); } if (frags.back) { if (frags.back->faces.empty()) { FError("Surface with no faces"); } - backlist.emplace_back(std::move(*frags.back)); + backlist.emplace_back(std::move(frags.back)); } } @@ -956,7 +957,7 @@ node_t *SolidBSP(mapentity_t *entity, bool midsplit) int visible_brush_sides = 0; int invisible_brush_sides = 0; for (const auto &brush : entity->brushes) { - for (auto &side : brush.faces) { + for (auto &side : brush->faces) { if (side.visible) { ++visible_brush_sides; } else { @@ -989,10 +990,10 @@ node_t *SolidBSP(mapentity_t *entity, bool midsplit) mapbrushes = entity->brushes.size(); // set the original pointers - std::vector brushcopies; - for (brush_t &original : entity->brushes) { - brush_t copy = original; - copy.original = &original; + std::vector> brushcopies; + for (const auto &original : entity->brushes) { + auto copy = std::make_unique(*original); + copy->original = original.get(); brushcopies.push_back(std::move(copy)); } PartitionBrushes(std::move(brushcopies), headnode); diff --git a/qbsp/surfaces.cc b/qbsp/surfaces.cc index 9ca28ade..cce1906c 100644 --- a/qbsp/surfaces.cc +++ b/qbsp/surfaces.cc @@ -702,13 +702,13 @@ void MakeVisibleFaces(mapentity_t* entity, node_t* headnode) c_nodefaces = 0; for (auto &brush : entity->brushes) { - for (auto &face : brush.faces) { + for (auto &face : brush->faces) { if (!face.visible) { continue; } face_t *temp = CopyFace(&face); - AddFaceToTree_r(entity, temp, &brush, headnode); + AddFaceToTree_r(entity, temp, brush.get(), headnode); } } diff --git a/qbsp/test_qbsp.cc b/qbsp/test_qbsp.cc index 9eca5f00..37b071c1 100644 --- a/qbsp/test_qbsp.cc +++ b/qbsp/test_qbsp.cc @@ -1,6 +1,7 @@ #include #include +#include #include #include #include