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:
parent
69aad26097
commit
84870cf366
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
86
qbsp/qbsp.cc
86
qbsp/qbsp.cc
|
|
@ -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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
|||
12
qbsp/tree.cc
12
qbsp/tree.cc
|
|
@ -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();
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
|
|
|
|||
Loading…
Reference in New Issue