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<bool> for the ternary value to store split type, since it's more explicit and obvious now what the three values do
This commit is contained in:
Jonathan 2022-08-19 15:15:53 -04:00
parent 69aad26097
commit 84870cf366
11 changed files with 158 additions and 133 deletions

View File

@ -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<tree_t> BrushBSP(mapentity_t &entity, const bspbrush_t::container &brushes, std::optional<bool> forced_quick_tree);
void BrushBSP(tree_t &tree, mapentity_t &entity, const bspbrush_t::container &brushes, tree_split_t split_type);

View File

@ -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<node_t *> 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);

View File

@ -72,10 +72,10 @@ bool Portal_EntityFlood(const portal_t *p, int32_t s);
enum class portaltype_t {
TREE, VIS
};
std::list<buildportal_t> MakeTreePortals_r(tree_t *tree, node_t *node, portaltype_t type, std::list<buildportal_t> boundary_portals, portalstats_t &stats, logging::percent_clock &clock);
void MakeTreePortals(tree_t *tree);
std::list<buildportal_t> MakeHeadnodePortals(tree_t *tree);
void MakePortalsFromBuildportals(tree_t *tree, std::list<buildportal_t> &buildportals);
std::list<buildportal_t> MakeTreePortals_r(node_t *node, portaltype_t type, std::list<buildportal_t> boundary_portals, portalstats_t &stats, logging::percent_clock &clock);
void MakeTreePortals(tree_t &tree);
std::list<buildportal_t> MakeHeadnodePortals(tree_t &tree);
void MakePortalsFromBuildportals(tree_t &tree, std::list<buildportal_t> &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);

View File

@ -24,6 +24,6 @@
#include <string_view>
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);

View File

@ -32,10 +32,13 @@
#include <tbb/concurrent_vector.h>
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);

View File

@ -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<size_t> SelectSplitPlane(const bspbrush_t::container &brushes, node_t *node, std::optional<bool> forced_quick_tree, bspstats_t &stats)
static std::optional<size_t> 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<bool> 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<tree_t> BrushBSP(mapentity_t &entity, const bspbrush_t::container &brushlist, std::optional<bool> 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<tree_t>();
if (brushlist.empty()) {
/*
* We allow an entity to be constructed with no visible brushes
@ -1193,24 +1193,24 @@ std::unique_ptr<tree_t> 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<tree_t> 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;
}

View File

@ -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<std::pair<node_t *, int>> 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);
}

View File

@ -125,18 +125,18 @@ MakeHeadnodePortals
The created portals will face the global outside_node
================
*/
std::list<buildportal_t> MakeHeadnodePortals(tree_t *tree)
std::list<buildportal_t> MakeHeadnodePortals(tree_t &tree)
{
int i, j, n;
std::array<buildportal_t, 6> 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<buildportal_t> MakeHeadnodePortals(tree_t *tree)
p.winding = BaseWindingForPlane<winding_t>(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<std::list<buildportal_t>> SplitNodePortals(const node_t *node, s
MakePortalsFromBuildportals
================
*/
void MakePortalsFromBuildportals(tree_t *tree, std::list<buildportal_t> &buildportals)
void MakePortalsFromBuildportals(tree_t &tree, std::list<buildportal_t> &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<buildportal_t> MakeTreePortals_r(tree_t *tree, node_t *node, portaltype_t type, std::list<buildportal_t> boundary_portals, portalstats_t &stats, logging::percent_clock &clock)
std::list<buildportal_t> MakeTreePortals_r(node_t *node, portaltype_t type, std::list<buildportal_t> boundary_portals, portalstats_t &stats, logging::percent_clock &clock)
{
clock();
@ -479,8 +479,8 @@ std::list<buildportal_t> MakeTreePortals_r(tree_t *tree, node_t *node, portaltyp
std::list<buildportal_t> 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<buildportal_t> 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);

View File

@ -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);
}

View File

@ -474,109 +474,109 @@ static void ProcessEntity(mapentity_t &entity, hull_index_t hullnum)
return;
}
std::unique_ptr<tree_t> 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<bool>(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);
}
/*

View File

@ -61,16 +61,18 @@ static void ClearNodePortals_r(node_t *node)
#include <tbb/parallel_for_each.h>
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_t> &portal) {
tbb::parallel_for_each(tree.portals, [](std::unique_ptr<portal_t> &portal) {
portal.reset();
});
tree->portals.clear();
tree.portals.clear();
}
//============================================================================