From 84870cf36617f9a53c525e24a06f501380b8113a Mon Sep 17 00:00:00 2001 From: Jonathan Date: Fri, 19 Aug 2022 15:15:53 -0400 Subject: [PATCH] since tree_t only exists as a type that only allocates heap memory, we don't need to wrap it in a unique_ptr; this also allows the vectors to keep their memory on the next pass, which may improve performance for huge maps removed tree_t parameter from functions that don't need it pass reference to tree_t instead of ptr use an enum instead of std::optional for the ternary value to store split type, since it's more explicit and obvious now what the three values do --- include/qbsp/brushbsp.hh | 14 ++++++- include/qbsp/outside.hh | 4 +- include/qbsp/portals.hh | 10 ++--- include/qbsp/prtfile.hh | 4 +- include/qbsp/tree.hh | 17 +++++++- qbsp/brushbsp.cc | 64 +++++++++++++++--------------- qbsp/outside.cc | 22 +++++----- qbsp/portals.cc | 46 ++++++++++----------- qbsp/prtfile.cc | 12 +++--- qbsp/qbsp.cc | 86 ++++++++++++++++++++-------------------- qbsp/tree.cc | 12 +++--- 11 files changed, 158 insertions(+), 133 deletions(-) diff --git a/include/qbsp/brushbsp.hh b/include/qbsp/brushbsp.hh index 0cf3a843..a482c510 100644 --- a/include/qbsp/brushbsp.hh +++ b/include/qbsp/brushbsp.hh @@ -86,5 +86,17 @@ bool WindingIsHuge(const T &w) return false; } +enum tree_split_t +{ + // change the split type depending on node size, + // brush count, etc + AUTO, + // always use the precise/expensive split method + // to make a good BSP tree + PRECISE, + // always use faster methods to create the tree + FAST +}; + bspbrush_t::ptr BrushFromBounds(const aabb3d &bounds); -std::unique_ptr BrushBSP(mapentity_t &entity, const bspbrush_t::container &brushes, std::optional forced_quick_tree); +void BrushBSP(tree_t &tree, mapentity_t &entity, const bspbrush_t::container &brushes, tree_split_t split_type); diff --git a/include/qbsp/outside.hh b/include/qbsp/outside.hh index bfa6f818..2cdb5283 100644 --- a/include/qbsp/outside.hh +++ b/include/qbsp/outside.hh @@ -27,7 +27,7 @@ struct node_t; struct tree_t; -bool FillOutside(tree_t *tree, hull_index_t hullnum, bspbrush_t::container &brushes); +bool FillOutside(tree_t &tree, hull_index_t hullnum, bspbrush_t::container &brushes); std::vector FindOccupiedClusters(node_t *headnode); -void FillBrushEntity(tree_t *tree, hull_index_t hullnum, bspbrush_t::container &brushes); +void FillBrushEntity(tree_t &tree, hull_index_t hullnum, bspbrush_t::container &brushes); diff --git a/include/qbsp/portals.hh b/include/qbsp/portals.hh index 2d58e871..2ecf4cbf 100644 --- a/include/qbsp/portals.hh +++ b/include/qbsp/portals.hh @@ -72,10 +72,10 @@ bool Portal_EntityFlood(const portal_t *p, int32_t s); enum class portaltype_t { TREE, VIS }; -std::list MakeTreePortals_r(tree_t *tree, node_t *node, portaltype_t type, std::list boundary_portals, portalstats_t &stats, logging::percent_clock &clock); -void MakeTreePortals(tree_t *tree); -std::list MakeHeadnodePortals(tree_t *tree); -void MakePortalsFromBuildportals(tree_t *tree, std::list &buildportals); +std::list MakeTreePortals_r(node_t *node, portaltype_t type, std::list boundary_portals, portalstats_t &stats, logging::percent_clock &clock); +void MakeTreePortals(tree_t &tree); +std::list MakeHeadnodePortals(tree_t &tree); +void MakePortalsFromBuildportals(tree_t &tree, std::list &buildportals); void EmitAreaPortals(node_t *headnode); void FloodAreas(node_t *headnode); -void MarkVisibleSides(tree_t *tree, bspbrush_t::container &brushes); +void MarkVisibleSides(tree_t &tree, bspbrush_t::container &brushes); diff --git a/include/qbsp/prtfile.hh b/include/qbsp/prtfile.hh index 9a8b2767..71da551d 100644 --- a/include/qbsp/prtfile.hh +++ b/include/qbsp/prtfile.hh @@ -24,6 +24,6 @@ #include struct tree_t; -void WritePortalFile(tree_t *tree); -void WriteDebugTreePortalFile(tree_t *tree, std::string_view filename_suffix); +void WritePortalFile(tree_t &tree); +void WriteDebugTreePortalFile(tree_t &tree, std::string_view filename_suffix); diff --git a/include/qbsp/tree.hh b/include/qbsp/tree.hh index 724e4f39..0c37d2b9 100644 --- a/include/qbsp/tree.hh +++ b/include/qbsp/tree.hh @@ -32,10 +32,13 @@ #include struct portal_t; +struct tree_t; + +void FreeTreePortals(tree_t &tree); struct tree_t { - node_t *headnode; + node_t *headnode = nullptr; node_t outside_node = {}; // portals outside the world face this aabb3d bounds; @@ -55,8 +58,18 @@ struct tree_t // creates a new node owned by `this` (stored in the `nodes` vector) and // returns a raw pointer to it node_t *create_node(); + + // reset the tree without clearing allocated vector space + void clear() + { + headnode = nullptr; + outside_node = {}; + bounds = {}; + + FreeTreePortals(*this); + nodes.clear(); + } }; -void FreeTreePortals(tree_t *tree); void DetailToSolid(node_t *node); void PruneNodes(node_t *node); diff --git a/qbsp/brushbsp.cc b/qbsp/brushbsp.cc index e51d1c62..a6488937 100644 --- a/qbsp/brushbsp.cc +++ b/qbsp/brushbsp.cc @@ -888,37 +888,39 @@ Using heuristics, chooses a plane to partition the brushes with. Returns nullopt if there are no valid planes to split with. ================ */ -static std::optional SelectSplitPlane(const bspbrush_t::container &brushes, node_t *node, std::optional forced_quick_tree, bspstats_t &stats) +static std::optional SelectSplitPlane(const bspbrush_t::container &brushes, node_t *node, tree_split_t split_type, bspstats_t &stats) { // no brushes left to split, so we can't use any plane. if (!brushes.size()) { return std::nullopt; } - // if forced_quick_tree is nullopt, we will choose fast/slow based on - // certain parameters. - if (!forced_quick_tree.has_value() || forced_quick_tree.value() == true) { - if (!forced_quick_tree.has_value()) { + if (split_type != tree_split_t::PRECISE) { + if (split_type == tree_split_t::AUTO) { // decide if we should switch to the midsplit method if (qbsp_options.midsplitbrushfraction.value() != 0.0) { // new way (opt-in) // how much of the map are we partitioning? double fractionOfMap = brushes.size() / (double) map.total_brushes; - forced_quick_tree = (fractionOfMap > qbsp_options.midsplitbrushfraction.value()); + if (fractionOfMap > qbsp_options.midsplitbrushfraction.value()) { + split_type = tree_split_t::FAST; + } } else { // old way (ericw-tools 0.15.2+) if (qbsp_options.maxnodesize.value() >= 64) { const vec_t maxnodesize = qbsp_options.maxnodesize.value() - qbsp_options.epsilon.value(); - forced_quick_tree = (node->bounds.maxs()[0] - node->bounds.mins()[0]) > maxnodesize - || (node->bounds.maxs()[1] - node->bounds.mins()[1]) > maxnodesize - || (node->bounds.maxs()[2] - node->bounds.mins()[2]) > maxnodesize; + if ((node->bounds.maxs()[0] - node->bounds.mins()[0]) > maxnodesize + || (node->bounds.maxs()[1] - node->bounds.mins()[1]) > maxnodesize + || (node->bounds.maxs()[2] - node->bounds.mins()[2]) > maxnodesize) { + split_type = tree_split_t::FAST; + } } } } - if (forced_quick_tree.value()) { + if (split_type == tree_split_t::FAST) { if (auto mid_plane = ChooseMidPlaneFromList(brushes, node)) { stats.c_midsplit++; @@ -1117,10 +1119,10 @@ BuildTree_r Called in parallel. ================== */ -static void BuildTree_r(tree_t *tree, node_t *node, bspbrush_t::container brushes, std::optional forced_quick_tree, bspstats_t &stats, logging::percent_clock &clock) +static void BuildTree_r(tree_t &tree, node_t *node, bspbrush_t::container brushes, tree_split_t split_type, bspstats_t &stats, logging::percent_clock &clock) { // find the best plane to use as a splitter - auto bestplane = SelectSplitPlane(brushes, node, forced_quick_tree, stats); + auto bestplane = SelectSplitPlane(brushes, node, split_type, stats); if (!bestplane) { // this is a leaf node @@ -1149,7 +1151,7 @@ static void BuildTree_r(tree_t *tree, node_t *node, bspbrush_t::container brushe // allocate children before recursing for (int i = 0; i < 2; i++) { - auto &newnode = node->children[i] = tree->create_node(); + auto &newnode = node->children[i] = tree.create_node(); newnode->parent = node; newnode->bounds = node->bounds; } @@ -1170,8 +1172,8 @@ static void BuildTree_r(tree_t *tree, node_t *node, bspbrush_t::container brushe // recursively process children tbb::task_group g; - g.run([&]() { BuildTree_r(tree, node->children[0], std::move(children[0]), forced_quick_tree, stats, clock); }); - g.run([&]() { BuildTree_r(tree, node->children[1], std::move(children[1]), forced_quick_tree, stats, clock); }); + g.run([&]() { BuildTree_r(tree, node->children[0], std::move(children[0]), split_type, stats, clock); }); + g.run([&]() { BuildTree_r(tree, node->children[1], std::move(children[1]), split_type, stats, clock); }); g.wait(); } @@ -1180,12 +1182,10 @@ static void BuildTree_r(tree_t *tree, node_t *node, bspbrush_t::container brushe BrushBSP ================== */ -std::unique_ptr BrushBSP(mapentity_t &entity, const bspbrush_t::container &brushlist, std::optional forced_quick_tree) +void BrushBSP(tree_t &tree, mapentity_t &entity, const bspbrush_t::container &brushlist, tree_split_t split_type) { logging::header(__func__ ); - auto tree = std::make_unique(); - if (brushlist.empty()) { /* * We allow an entity to be constructed with no visible brushes @@ -1193,24 +1193,24 @@ std::unique_ptr BrushBSP(mapentity_t &entity, const bspbrush_t::containe * collision hull for the engine. Probably could be done a little * smarter, but this works. */ - auto headnode = tree->create_node(); + auto headnode = tree.create_node(); headnode->bounds = entity.bounds; // The choice of plane is mostly unimportant, but having it at (0, 0, 0) affects // the node bounds calculation. headnode->planenum = 0; - headnode->children[0] = tree->create_node(); + headnode->children[0] = tree.create_node(); headnode->children[0]->is_leaf = true; headnode->children[0]->contents = qbsp_options.target_game->create_empty_contents(); headnode->children[0]->parent = headnode; - headnode->children[1] = tree->create_node(); + headnode->children[1] = tree.create_node(); headnode->children[1]->is_leaf = true; headnode->children[1]->contents = qbsp_options.target_game->create_empty_contents(); headnode->children[1]->parent = headnode; - tree->bounds = headnode->bounds; - tree->headnode = headnode; + tree.bounds = headnode->bounds; + tree.headnode = headnode; - return tree; + return; } size_t c_faces = 0; @@ -1247,30 +1247,28 @@ std::unique_ptr BrushBSP(mapentity_t &entity, const bspbrush_t::containe c_nonvisfaces++; } - tree->bounds += b->bounds; + tree.bounds += b->bounds; } logging::print(logging::flag::STAT, " {:8} brushes\n", c_brushes); logging::print(logging::flag::STAT, " {:8} visible faces\n", c_faces); logging::print(logging::flag::STAT, " {:8} nonvisible faces\n", c_nonvisfaces); - auto node = tree->create_node(); + auto node = tree.create_node(); + + node->bounds = tree.bounds.grow(SIDESPACE); + node->volume = BrushFromBounds(node->bounds); - node->volume = BrushFromBounds(tree->bounds.grow(SIDESPACE)); - node->bounds = tree->bounds.grow(SIDESPACE); - - tree->headnode = node; + tree.headnode = node; bspstats_t stats{}; stats.leafstats = qbsp_options.target_game->create_content_stats(); { logging::percent_clock clock; - BuildTree_r(tree.get(), tree->headnode, brushlist, forced_quick_tree, stats, clock); + BuildTree_r(tree, tree.headnode, brushlist, split_type, stats, clock); } logging::header("CountLeafs"); qbsp_options.target_game->print_content_stats(*stats.leafstats, "leafs"); - - return tree; } diff --git a/qbsp/outside.cc b/qbsp/outside.cc index 2b94e8ac..425db948 100644 --- a/qbsp/outside.cc +++ b/qbsp/outside.cc @@ -132,7 +132,7 @@ preconditions: - all leafs have outside_distance set to -1 ================== */ -static void FloodFillClustersFromVoid(tree_t *tree) +static void FloodFillClustersFromVoid(tree_t &tree) { // breadth-first search std::list> queue; @@ -141,10 +141,10 @@ static void FloodFillClustersFromVoid(tree_t *tree) // push a node onto the queue which is in the void, but has a portal to outside_node // NOTE: remember, the headnode has no relationship to the outside of the map. { - const int side = (tree->outside_node.portals->nodes[0] == &tree->outside_node); - node_t *fillnode = tree->outside_node.portals->nodes[side]; + const int side = (tree.outside_node.portals->nodes[0] == &tree.outside_node); + node_t *fillnode = tree.outside_node.portals->nodes[side]; - Q_assert(fillnode != &tree->outside_node); + Q_assert(fillnode != &tree.outside_node); // this must be true because the map is made from closed brushes, beyond which is void Q_assert(!LeafSealsMap(fillnode)); @@ -584,9 +584,9 @@ get incorrectly marked as "invisible"). Special cases: structural fully covered by detail still needs to be marked "visible". =========== */ -bool FillOutside(tree_t *tree, hull_index_t hullnum, bspbrush_t::container &brushes) +bool FillOutside(tree_t &tree, hull_index_t hullnum, bspbrush_t::container &brushes) { - node_t *node = tree->headnode; + node_t *node = tree.headnode; logging::funcheader(); logging::percent_clock clock; @@ -621,8 +621,8 @@ bool FillOutside(tree_t *tree, hull_index_t hullnum, bspbrush_t::container &brus BFSFloodFillFromOccupiedLeafs(occupied_clusters); /* first check to see if an occupied leaf is hit */ - const int side = (tree->outside_node.portals->nodes[0] == &tree->outside_node); - node_t *fillnode = tree->outside_node.portals->nodes[side]; + const int side = (tree.outside_node.portals->nodes[0] == &tree.outside_node); + node_t *fillnode = tree.outside_node.portals->nodes[side]; if (fillnode->occupied > 0) { leakline = MakeLeakLine(fillnode, leakentity); @@ -705,14 +705,14 @@ bool FillOutside(tree_t *tree, hull_index_t hullnum, bspbrush_t::container &brus return true; } -void FillBrushEntity(tree_t *tree, hull_index_t hullnum, bspbrush_t::container &brushes) +void FillBrushEntity(tree_t &tree, hull_index_t hullnum, bspbrush_t::container &brushes) { logging::funcheader(); // Clear the outside filling state on all nodes - ClearOccupied_r(tree->headnode); + ClearOccupied_r(tree.headnode); MarkBrushSidesInvisible(brushes); - MarkVisibleBrushSides_R(tree->headnode); + MarkVisibleBrushSides_R(tree.headnode); } diff --git a/qbsp/portals.cc b/qbsp/portals.cc index 0c247207..41b04374 100644 --- a/qbsp/portals.cc +++ b/qbsp/portals.cc @@ -125,18 +125,18 @@ MakeHeadnodePortals The created portals will face the global outside_node ================ */ -std::list MakeHeadnodePortals(tree_t *tree) +std::list MakeHeadnodePortals(tree_t &tree) { int i, j, n; std::array portals {}; qplane3d bplanes[6]; // pad with some space so there will never be null volume leafs - aabb3d bounds = tree->bounds.grow(SIDESPACE); + aabb3d bounds = tree.bounds.grow(SIDESPACE); - tree->outside_node.is_leaf = true; - tree->outside_node.contents = qbsp_options.target_game->create_solid_contents(); - tree->outside_node.portals = NULL; + tree.outside_node.is_leaf = true; + tree.outside_node.contents = qbsp_options.target_game->create_solid_contents(); + tree.outside_node.portals = NULL; // create 6 portals forming a cube around the bounds of the map. // these portals will have `outside_node` on one side, and headnode on the other. @@ -159,9 +159,9 @@ std::list MakeHeadnodePortals(tree_t *tree) p.winding = BaseWindingForPlane(pl); if (side) { - p.nodes = { &tree->outside_node, tree->headnode }; + p.nodes = { &tree.outside_node, tree.headnode }; } else { - p.nodes = { tree->headnode, &tree->outside_node }; + p.nodes = { tree.headnode, &tree.outside_node }; } } @@ -362,12 +362,12 @@ static twosided> SplitNodePortals(const node_t *node, s MakePortalsFromBuildportals ================ */ -void MakePortalsFromBuildportals(tree_t *tree, std::list &buildportals) +void MakePortalsFromBuildportals(tree_t &tree, std::list &buildportals) { - tree->portals.reserve(buildportals.size()); + tree.portals.reserve(buildportals.size()); for (auto &buildportal : buildportals) { - portal_t *new_portal = tree->create_portal(); + portal_t *new_portal = tree.create_portal(); new_portal->plane = buildportal.plane; new_portal->onnode = buildportal.onnode; new_portal->winding = std::move(buildportal.winding); @@ -394,15 +394,15 @@ inline void CalcNodeBounds(node_t *node) } } -static void CalcTreeBounds_r(tree_t *tree, node_t *node, logging::percent_clock &clock) +static void CalcTreeBounds_r(node_t *node, logging::percent_clock &clock) { if (node->is_leaf) { clock(); CalcNodeBounds(node); } else { tbb::task_group g; - g.run([&]() { CalcTreeBounds_r(tree, node->children[0], clock); }); - g.run([&]() { CalcTreeBounds_r(tree, node->children[1], clock); }); + g.run([&]() { CalcTreeBounds_r(node->children[0], clock); }); + g.run([&]() { CalcTreeBounds_r(node->children[1], clock); }); g.wait(); node->bounds = node->children[0]->bounds + node->children[1]->bounds; @@ -460,7 +460,7 @@ MakeTreePortals_r Given the list of portals bounding `node`, returns the portal list for a fully-portalized `node`. ================== */ -std::list MakeTreePortals_r(tree_t *tree, node_t *node, portaltype_t type, std::list boundary_portals, portalstats_t &stats, logging::percent_clock &clock) +std::list MakeTreePortals_r(node_t *node, portaltype_t type, std::list boundary_portals, portalstats_t &stats, logging::percent_clock &clock) { clock(); @@ -479,8 +479,8 @@ std::list MakeTreePortals_r(tree_t *tree, node_t *node, portaltyp std::list result_portals_front, result_portals_back; tbb::task_group g; - g.run([&]() { result_portals_front = MakeTreePortals_r(tree, node->children[0], type, std::move(boundary_portals_split.front), stats, clock); }); - g.run([&]() { result_portals_back = MakeTreePortals_r(tree, node->children[1], type, std::move(boundary_portals_split.back), stats, clock); }); + g.run([&]() { result_portals_front = MakeTreePortals_r(node->children[0], type, std::move(boundary_portals_split.front), stats, clock); }); + g.run([&]() { result_portals_back = MakeTreePortals_r(node->children[1], type, std::move(boundary_portals_split.back), stats, clock); }); g.wait(); // sequential part: push the nodeportal down each side of the bsp so it connects leafs @@ -510,7 +510,7 @@ std::list MakeTreePortals_r(tree_t *tree, node_t *node, portaltyp MakeTreePortals ================== */ -void MakeTreePortals(tree_t *tree) +void MakeTreePortals(tree_t &tree) { logging::funcheader(); @@ -519,11 +519,11 @@ void MakeTreePortals(tree_t *tree) auto headnodeportals = MakeHeadnodePortals(tree); { - logging::percent_clock clock(tree->nodes.size()); + logging::percent_clock clock(tree.nodes.size()); portalstats_t stats{}; - auto buildportals = MakeTreePortals_r(tree, tree->headnode, portaltype_t::TREE, std::move(headnodeportals), stats, clock); + auto buildportals = MakeTreePortals_r(tree.headnode, portaltype_t::TREE, std::move(headnodeportals), stats, clock); MakePortalsFromBuildportals(tree, buildportals); } @@ -531,10 +531,10 @@ void MakeTreePortals(tree_t *tree) logging::header("CalcTreeBounds"); logging::percent_clock clock; - CalcTreeBounds_r(tree, tree->headnode, clock); + CalcTreeBounds_r(tree.headnode, clock); clock.print(); - logging::print(logging::flag::STAT, " {:8} tree portals\n", tree->portals.size()); + logging::print(logging::flag::STAT, " {:8} tree portals\n", tree.portals.size()); } /* @@ -906,7 +906,7 @@ MarkVisibleSides ============= */ -void MarkVisibleSides(tree_t *tree, bspbrush_t::container &brushes) +void MarkVisibleSides(tree_t &tree, bspbrush_t::container &brushes) { logging::funcheader(); @@ -922,7 +922,7 @@ void MarkVisibleSides(tree_t *tree, bspbrush_t::container &brushes) size_t num_sides_not_found = 0; // set visible flags on the sides that are used by portals - MarkVisibleSides_r(tree->headnode, num_sides_not_found); + MarkVisibleSides_r(tree.headnode, num_sides_not_found); if (num_sides_not_found) { logging::print("WARNING: sides not found for {} portals\n", num_sides_not_found); diff --git a/qbsp/prtfile.cc b/qbsp/prtfile.cc index 0c9441c0..1a6846b2 100644 --- a/qbsp/prtfile.cc +++ b/qbsp/prtfile.cc @@ -255,7 +255,7 @@ static void WritePortalfile(node_t *headnode, portal_state_t *state) WritePortalFile ================== */ -void WritePortalFile(tree_t *tree) +void WritePortalFile(tree_t &tree) { logging::funcheader(); @@ -270,13 +270,13 @@ void WritePortalFile(tree_t *tree) // vis portal generation doesn't use headnode portals portalstats_t stats{}; - auto buildportals = MakeTreePortals_r(tree, tree->headnode, portaltype_t::VIS, {}, stats, clock); + auto buildportals = MakeTreePortals_r(tree.headnode, portaltype_t::VIS, {}, stats, clock); MakePortalsFromBuildportals(tree, buildportals); } /* save portal file for vis tracing */ - WritePortalfile(tree->headnode, &state); + WritePortalfile(tree.headnode, &state); logging::print(logging::flag::STAT, " {:8} vis leafs\n", state.num_visleafs); logging::print(logging::flag::STAT, " {:8} vis clusters\n", state.num_visclusters); @@ -343,12 +343,12 @@ static void CountTreePortals_r(node_t *node, size_t &count) WritePortalFile ================== */ -void WriteDebugTreePortalFile(tree_t *tree, std::string_view filename_suffix) +void WriteDebugTreePortalFile(tree_t &tree, std::string_view filename_suffix) { logging::funcheader(); size_t portal_count = 0; - CountTreePortals_r(tree->headnode, portal_count); + CountTreePortals_r(tree.headnode, portal_count); // write the file fs::path name = qbsp_options.bsp_path; @@ -361,7 +361,7 @@ void WriteDebugTreePortalFile(tree_t *tree, std::string_view filename_suffix) fmt::print(portalFile, "PRT1\n"); fmt::print(portalFile, "{}\n", 0); fmt::print(portalFile, "{}\n", portal_count); - WriteTreePortals_r(tree->headnode, portalFile); + WriteTreePortals_r(tree.headnode, portalFile); logging::print(logging::flag::STAT, " {:8} tree portals written to {}\n", portal_count, name); } diff --git a/qbsp/qbsp.cc b/qbsp/qbsp.cc index d64d39c3..1716dde2 100644 --- a/qbsp/qbsp.cc +++ b/qbsp/qbsp.cc @@ -474,109 +474,109 @@ static void ProcessEntity(mapentity_t &entity, hull_index_t hullnum) return; } - std::unique_ptr tree = nullptr; - // simpler operation for hulls if (hullnum.value_or(0)) { - tree = BrushBSP(entity, brushes, true); + tree_t tree; + BrushBSP(tree, entity, brushes, tree_split_t::FAST); if (map.is_world_entity(entity) && !qbsp_options.nofill.value()) { // assume non-world bmodels are simple - MakeTreePortals(tree.get()); - if (FillOutside(tree.get(), hullnum, brushes)) { + MakeTreePortals(tree); + if (FillOutside(tree, hullnum, brushes)) { // make a really good tree - tree.reset(); - tree = BrushBSP(entity, brushes, std::nullopt); + tree.clear(); + BrushBSP(tree, entity, brushes, tree_split_t::AUTO); // fill again so PruneNodes works - MakeTreePortals(tree.get()); - FillOutside(tree.get(), hullnum, brushes); - FreeTreePortals(tree.get()); - PruneNodes(tree->headnode); + MakeTreePortals(tree); + FillOutside(tree, hullnum, brushes); + FreeTreePortals(tree); + PruneNodes(tree.headnode); } } - ExportClipNodes(entity, tree->headnode, hullnum.value()); + ExportClipNodes(entity, tree.headnode, hullnum.value()); return; } // full operation for collision (or main hull) + tree_t tree; - if (qbsp_options.forcegoodtree.value()) { - tree = BrushBSP(entity, brushes, false); - } else { - tree = BrushBSP(entity, brushes, map.is_world_entity(entity) ? std::nullopt : std::optional(false)); - } + BrushBSP(tree, entity, brushes, + qbsp_options.forcegoodtree.value() ? tree_split_t::PRECISE : // we asked for the slow method + !map.is_world_entity(entity) ? tree_split_t::FAST : // brush models are assumed to be simple + tree_split_t::AUTO); // build all the portals in the bsp tree // some portals are solid polygons, and some are paths to other leafs - MakeTreePortals(tree.get()); + MakeTreePortals(tree); if (map.is_world_entity(entity)) { // flood fills from the void. // marks brush sides which are *only* touching void; // we can skip using them as BSP splitters on the "really good tree" // (effectively expanding those brush sides outwards). - if (!qbsp_options.nofill.value() && FillOutside(tree.get(), hullnum, brushes)) { + if (!qbsp_options.nofill.value() && FillOutside(tree, hullnum, brushes)) { // make a really good tree - tree.reset(); - tree = BrushBSP(entity, brushes, false); + tree.clear(); + BrushBSP(tree, entity, brushes, tree_split_t::PRECISE); // make the real portals for vis tracing - MakeTreePortals(tree.get()); + MakeTreePortals(tree); // fill again so PruneNodes works - FillOutside(tree.get(), hullnum, brushes); + FillOutside(tree, hullnum, brushes); } // Area portals if (qbsp_options.target_game->id == GAME_QUAKE_II) { - FloodAreas(tree->headnode); - EmitAreaPortals(tree->headnode); + FloodAreas(tree.headnode); + EmitAreaPortals(tree.headnode); } } else { - FillBrushEntity(tree.get(), hullnum, brushes); + FillBrushEntity(tree, hullnum, brushes); // rebuild BSP now that we've marked invisible brush sides - tree.reset(); - tree = BrushBSP(entity, brushes, false); + tree.clear(); + BrushBSP(tree, entity, brushes, tree_split_t::PRECISE); } - MakeTreePortals(tree.get()); + MakeTreePortals(tree); - MarkVisibleSides(tree.get(), brushes); - MakeFaces(tree->headnode); + MarkVisibleSides(tree, brushes); + MakeFaces(tree.headnode); - FreeTreePortals(tree.get()); - PruneNodes(tree->headnode); + FreeTreePortals(tree); + PruneNodes(tree.headnode); // write out .prt for main hull if (!hullnum.value_or(0) && map.is_world_entity(entity) && (!map.leakfile || qbsp_options.keepprt.value())) { - WritePortalFile(tree.get()); + WritePortalFile(tree); } // needs to come after any face creation - MakeMarkFaces(tree->headnode); + MakeMarkFaces(tree.headnode); - CountLeafs(tree->headnode); + CountLeafs(tree.headnode); // output vertices first, since TJunc needs it - EmitVertices(tree->headnode); + EmitVertices(tree.headnode); - TJunc(tree->headnode); + TJunc(tree.headnode); if (qbsp_options.objexport.value() && map.is_world_entity(entity)) { - ExportObj_Nodes("pre_makefaceedges_plane_faces", tree->headnode); - ExportObj_Marksurfaces("pre_makefaceedges_marksurfaces", tree->headnode); + ExportObj_Nodes("pre_makefaceedges_plane_faces", tree.headnode); + ExportObj_Marksurfaces("pre_makefaceedges_marksurfaces", tree.headnode); } Q_assert(!entity.firstoutputfacenumber.has_value()); - entity.firstoutputfacenumber = MakeFaceEdges(tree->headnode); + entity.firstoutputfacenumber = MakeFaceEdges(tree.headnode); if (qbsp_options.target_game->id == GAME_QUAKE_II) { - ExportBrushList(entity, tree->headnode); + ExportBrushList(entity, tree.headnode); } - ExportDrawNodes(entity, tree->headnode, entity.firstoutputfacenumber.value()); + ExportDrawNodes(entity, tree.headnode, entity.firstoutputfacenumber.value()); + FreeTreePortals(tree); } /* diff --git a/qbsp/tree.cc b/qbsp/tree.cc index 7bec5456..ba613093 100644 --- a/qbsp/tree.cc +++ b/qbsp/tree.cc @@ -61,16 +61,18 @@ static void ClearNodePortals_r(node_t *node) #include -void FreeTreePortals(tree_t *tree) +void FreeTreePortals(tree_t &tree) { - ClearNodePortals_r(tree->headnode); - tree->outside_node.portals = nullptr; + if (tree.headnode) { + ClearNodePortals_r(tree.headnode); + tree.outside_node.portals = nullptr; + } - tbb::parallel_for_each(tree->portals, [](std::unique_ptr &portal) { + tbb::parallel_for_each(tree.portals, [](std::unique_ptr &portal) { portal.reset(); }); - tree->portals.clear(); + tree.portals.clear(); } //============================================================================