qbsp: move to unique_ptr<brush_t> vectors

This commit is contained in:
Eric Wasylishen 2022-05-03 02:33:24 -06:00
parent 0049449075
commit 893b0b080e
14 changed files with 153 additions and 120 deletions

View File

@ -33,6 +33,7 @@ struct brush_t
* the BrushBSP will have this pointing back to the original brush in mapentity_t::brushes. * the BrushBSP will have this pointing back to the original brush in mapentity_t::brushes.
*/ */
brush_t *original; brush_t *original;
uint32_t file_order;
aabb3d bounds; aabb3d bounds;
std::vector<face_t> faces; std::vector<face_t> faces;
contentflags_t contents; /* BSP contents */ contentflags_t contents; /* BSP contents */

View File

@ -21,14 +21,13 @@
#pragma once #pragma once
#include <qbsp/brush.hh>
#include <common/qvec.hh> #include <common/qvec.hh>
#include <list> #include <list>
#include <tuple> #include <tuple>
#include <vector> #include <vector>
struct brush_t;
struct face_t; struct face_t;
int MakeSkipTexinfo(); int MakeSkipTexinfo();
@ -38,4 +37,4 @@ face_t *MirrorFace(const face_t *face);
std::tuple<face_t *, face_t *> SplitFace(face_t *in, const qplane3d &split); std::tuple<face_t *, face_t *> SplitFace(face_t *in, const qplane3d &split);
void UpdateFaceSphere(face_t *in); void UpdateFaceSphere(face_t *in);
bool BrushGE(const brush_t &a, const brush_t &b); bool BrushGE(const brush_t &a, const brush_t &b);
std::vector<brush_t> ChopBrushes(const std::vector<brush_t> &input); std::vector<std::unique_ptr<brush_t>> ChopBrushes(const std::vector<std::unique_ptr<brush_t>> &input);

View File

@ -21,8 +21,9 @@
#pragma once #pragma once
#include <qbsp/brush.hh> #include <qbsp/qbsp.hh>
#include <common/bspfile.hh>
#include <common/parser.hh> #include <common/parser.hh>
#include "common/cmdlib.hh" #include "common/cmdlib.hh"
@ -32,6 +33,8 @@
#include <unordered_map> #include <unordered_map>
#include <list> #include <list>
struct brush_t;
struct qbsp_plane_t : qplane3d struct qbsp_plane_t : qplane3d
{ {
int type = 0; int type = 0;
@ -96,7 +99,7 @@ public:
std::vector<std::pair<std::string, std::string>> epairs; std::vector<std::pair<std::string, std::string>> epairs;
aabb3d bounds; aabb3d bounds;
std::vector<brush_t> brushes; std::vector<std::unique_ptr<brush_t>> brushes;
int firstoutputfacenumber = -1; int firstoutputfacenumber = -1;
std::optional<size_t> outputmodelnumber = std::nullopt; std::optional<size_t> outputmodelnumber = std::nullopt;
@ -241,6 +244,6 @@ void ExportObj_Brushes(const std::string &filesuffix, const std::vector<const br
void ExportObj_Nodes(const std::string &filesuffix, const node_t *nodes); void ExportObj_Nodes(const std::string &filesuffix, const node_t *nodes);
void ExportObj_Marksurfaces(const std::string &filesuffix, const node_t *nodes); void ExportObj_Marksurfaces(const std::string &filesuffix, const node_t *nodes);
void WriteBspBrushMap(const fs::path &name, const std::vector<brush_t> &list); void WriteBspBrushMap(const fs::path &name, const std::vector<std::unique_ptr<brush_t>> &list);
bool IsValidTextureProjection(const qvec3f &faceNormal, const qvec3f &s_vec, const qvec3f &t_vec); bool IsValidTextureProjection(const qvec3f &faceNormal, const qvec3f &s_vec, const qvec3f &t_vec);

View File

@ -21,6 +21,11 @@
#pragma once #pragma once
#include <vector>
class mapentity_t;
struct node_t;
bool FillOutside(mapentity_t *entity, node_t *node, const int hullnum); bool FillOutside(mapentity_t *entity, node_t *node, const int hullnum);
std::vector<node_t *> FindOccupiedClusters(node_t *headnode); std::vector<node_t *> FindOccupiedClusters(node_t *headnode);

View File

@ -21,21 +21,21 @@
#pragma once #pragma once
#include <qbsp/brush.hh>
#include <common/qvec.hh> #include <common/qvec.hh>
#include <atomic> #include <atomic>
#include <list> #include <list>
#include <optional> #include <optional>
#include <memory>
extern std::atomic<int> splitnodes; extern std::atomic<int> splitnodes;
struct brush_t;
struct node_t; struct node_t;
struct face_t; struct face_t;
class mapentity_t; class mapentity_t;
void DetailToSolid(node_t *node); void DetailToSolid(node_t *node);
void PruneNodes(node_t *node); void PruneNodes(node_t *node);
twosided<std::optional<brush_t>> SplitBrush(const brush_t &brush, const qplane3d &split); twosided<std::unique_ptr<brush_t>> SplitBrush(std::unique_ptr<brush_t> brush, const qplane3d &split);
node_t *SolidBSP(mapentity_t *entity, bool midsplit); node_t *SolidBSP(mapentity_t *entity, bool midsplit);

View File

@ -965,7 +965,7 @@ static void Brush_LoadEntity(mapentity_t *dst, const mapentity_t *src, const int
} else { } else {
stats.liquid++; stats.liquid++;
} }
dst->brushes.push_back(std::move(brush.value())); dst->brushes.push_back(std::make_unique<brush_t>(brush.value()));
dst->bounds += brush->bounds; dst->bounds += brush->bounds;
} }

View File

@ -215,21 +215,11 @@ static void FreeFaces(std::list<face_t *> &facelist)
//========================================================================== //==========================================================================
/* static std::vector<std::unique_ptr<brush_t>> SingleBrush(std::unique_ptr<brush_t> a)
==================
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)
{ {
Q_assert(brush >= entity->brushes.data()); std::vector<std::unique_ptr<brush_t>> res;
Q_assert(brush < (entity->brushes.data() + entity->brushes.size())); res.push_back(std::move(a));
return res;
return static_cast<int>(brush - entity->brushes.data());
} }
/* /*
@ -239,30 +229,46 @@ SubtractBrush
Returns the fragments from a - b Returns the fragments from a - b
================== ==================
*/ */
std::vector<brush_t> SubtractBrush(const brush_t& a, const brush_t& b) static std::vector<std::unique_ptr<brush_t>> SubtractBrush(std::unique_ptr<brush_t> a, const brush_t& b)
{ {
// first, check if `a` is fully in front of _any_ of b's planes // first, check if `a` is fully in front of _any_ of b's planes
for (const auto &side : b.faces) { for (const auto &side : b.faces) {
auto [front, back] = SplitBrush(a, Face_Plane(&side)); // is `a` fully in front of `side`?
if (front && !back) { 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 // `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<brush_t> frontlist; std::vector<std::unique_ptr<brush_t>> frontlist;
std::vector<brush_t> unclassified{a}; std::vector<std::unique_ptr<brush_t>> unclassified = SingleBrush(std::move(a));
for (const auto &side : b.faces) { for (const auto &side : b.faces) {
std::vector<brush_t> new_unclassified; std::vector<std::unique_ptr<brush_t>> new_unclassified;
for (const auto &fragment : unclassified) { for (auto &fragment : unclassified) {
auto [front, back] = SplitBrush(fragment, Face_Plane(&side)); // destructively processing `unclassified` here
auto [front, back] = SplitBrush(std::move(fragment), Face_Plane(&side));
if (front) { if (front) {
frontlist.push_back(*front); frontlist.push_back(std::move(front));
} }
if (back) { 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 // same contents clip each other
if (a.contents == b.contents && a.contents.clips_same_type()) { if (a.contents == b.contents && a.contents.clips_same_type()) {
// map file order // map file order
return &a > &b; return a.file_order > b.file_order;
} }
// only chop if at least one of the two contents is // 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) { if (a_pri == b_pri) {
// map file order // map file order
return &a > &b; return a.file_order > b.file_order;
} }
return a_pri >= b_pri; return a_pri >= b_pri;
@ -311,12 +317,13 @@ ChopBrushes
Clips off any overlapping portions of brushes Clips off any overlapping portions of brushes
================== ==================
*/ */
std::vector<brush_t> ChopBrushes(const std::vector<brush_t>& input) std::vector<std::unique_ptr<brush_t>> ChopBrushes(const std::vector<std::unique_ptr<brush_t>>& input)
{ {
logging::print(logging::flag::PROGRESS, "---- {} ----\n", __func__); logging::print(logging::flag::PROGRESS, "---- {} ----\n", __func__);
// output vector for the parallel_for // each inner vector corresponds to a brush in `input`
std::vector<std::vector<brush_t>> brush_fragments; // (set up this way for thread safety)
std::vector<std::vector<std::unique_ptr<brush_t>>> brush_fragments;
brush_fragments.resize(input.size()); brush_fragments.resize(input.size());
/* /*
@ -327,26 +334,31 @@ std::vector<brush_t> ChopBrushes(const std::vector<brush_t>& input)
* *
* The output of this is a face list for each brush called "outside" * The output of this is a face list for each brush called "outside"
*/ */
tbb::parallel_for(static_cast<size_t>(0), input.size(), [input, &brush_fragments](const size_t i) { tbb::parallel_for(static_cast<size_t>(0), input.size(), [&](const size_t i) {
const auto &brush = input[i]; const auto& brush = input[i];
// the fragments `brush` is chopped into // the fragments `brush` is chopped into
std::vector<brush_t> brush_result{brush}; std::vector<std::unique_ptr<brush_t>> brush_result = SingleBrush(
// start with a copy of brush
std::make_unique<brush_t>(*brush)
);
for (auto &clipbrush : input) { for (auto &clipbrush : input) {
if (&brush == &clipbrush) { if (brush == clipbrush) {
continue; continue;
} }
if (brush.bounds.disjoint(clipbrush.bounds)) { if (brush->bounds.disjoint(clipbrush->bounds)) {
continue; continue;
} }
if (BrushGE(clipbrush, brush)) { if (BrushGE(*clipbrush, *brush)) {
std::vector<brush_t> new_result; std::vector<std::unique_ptr<brush_t>> new_result;
// clipbrush is stronger. clip all existing fragments to clipbrush // clipbrush is stronger.
for (const auto &current_fragment : brush_result) { // rebuild existing fragments in brush_result, cliping them to clipbrush
for (const auto &new_fragment : SubtractBrush(current_fragment, clipbrush)) { for (auto &current_fragment : brush_result) {
new_result.push_back(new_fragment); for (auto &new_fragment : SubtractBrush(std::move(current_fragment), *clipbrush)) {
new_result.push_back(std::move(new_fragment));
} }
} }
@ -355,11 +367,11 @@ std::vector<brush_t> ChopBrushes(const std::vector<brush_t>& input)
} }
// save the result // save the result
brush_fragments[i] = brush_result; brush_fragments[i] = std::move(brush_result);
}); });
// Non parallel part: // Non parallel part:
std::vector<brush_t> result; std::vector<std::unique_ptr<brush_t>> result;
for (auto &fragment_list : brush_fragments) { for (auto &fragment_list : brush_fragments) {
for (auto &fragment : fragment_list) { for (auto &fragment : fragment_list) {
result.push_back(std::move(fragment)); result.push_back(std::move(fragment));

View File

@ -2271,7 +2271,7 @@ WriteBspBrushMap
from q3map from q3map
================== ==================
*/ */
void WriteBspBrushMap(const fs::path &name, const std::vector<brush_t> &list) void WriteBspBrushMap(const fs::path &name, const std::vector<std::unique_ptr<brush_t>> &list)
{ {
logging::print("writing {}\n", name); logging::print("writing {}\n", name);
std::ofstream f(name); std::ofstream f(name);
@ -2283,7 +2283,7 @@ void WriteBspBrushMap(const fs::path &name, const std::vector<brush_t> &list)
for (auto &brush : list) { for (auto &brush : list) {
fmt::print(f, "{{\n"); fmt::print(f, "{{\n");
for (auto &face : brush.faces) { for (auto &face : brush->faces) {
// FIXME: Factor out this mess // FIXME: Factor out this mess
qbsp_plane_t plane = map.planes.at(face.planenum); qbsp_plane_t plane = map.planes.at(face.planenum);
@ -2322,15 +2322,17 @@ from q3map
*/ */
static void TestExpandBrushes(const mapentity_t *src) static void TestExpandBrushes(const mapentity_t *src)
{ {
std::vector<brush_t> hull1brushes; std::vector<std::unique_ptr<brush_t>> hull1brushes;
for (int i = 0; i < src->nummapbrushes; i++) { for (int i = 0; i < src->nummapbrushes; i++) {
const mapbrush_t *mapbrush = &src->mapbrush(i); const mapbrush_t *mapbrush = &src->mapbrush(i);
std::optional<brush_t> hull1brush = LoadBrush( std::optional<brush_t> hull1brush = LoadBrush(
src, mapbrush, {CONTENTS_SOLID}, {}, rotation_t::none, options.target_game->id == GAME_QUAKE_II ? -1 : 1); src, mapbrush, {CONTENTS_SOLID}, {}, rotation_t::none, options.target_game->id == GAME_QUAKE_II ? -1 : 1);
if (hull1brush) if (hull1brush) {
hull1brushes.emplace_back(std::move(hull1brush.value())); hull1brushes.emplace_back(
std::make_unique<brush_t>(std::move(*hull1brush)));
}
} }
WriteBspBrushMap("expanded.map", hull1brushes); WriteBspBrushMap("expanded.map", hull1brushes);

View File

@ -19,6 +19,8 @@
See file, 'COPYING', for details. See file, 'COPYING', for details.
*/ */
#include <qbsp/outside.hh>
#include <qbsp/brush.hh>
#include <qbsp/map.hh> #include <qbsp/map.hh>
#include <qbsp/portals.hh> #include <qbsp/portals.hh>
#include <qbsp/qbsp.hh> #include <qbsp/qbsp.hh>
@ -357,7 +359,7 @@ std::vector<node_t *> FindOccupiedClusters(node_t *headnode)
static void MarkBrushSidesInvisible(mapentity_t *entity) static void MarkBrushSidesInvisible(mapentity_t *entity)
{ {
for (auto &brush : entity->brushes) { for (auto &brush : entity->brushes) {
for (auto &face : brush.faces) { for (auto &face : brush->faces) {
face.visible = false; face.visible = false;
} }
} }

View File

@ -20,6 +20,7 @@
*/ */
// portals.c // portals.c
#include <qbsp/brush.hh>
#include <qbsp/portals.hh> #include <qbsp/portals.hh>
#include <fstream> #include <fstream>
#include <fmt/ostream.h> #include <fmt/ostream.h>

View File

@ -349,12 +349,12 @@ static void ExportBrushList(mapentity_t *entity, node_t *node)
brush_state = {}; brush_state = {};
for (auto &b : entity->brushes) { for (auto &b : entity->brushes) {
b.outputnumber = { static_cast<uint32_t>(map.bsp.dbrushes.size()) }; b->outputnumber = {static_cast<uint32_t>(map.bsp.dbrushes.size())};
dbrush_t &brush = map.bsp.dbrushes.emplace_back( dbrush_t &brush = map.bsp.dbrushes.emplace_back(
dbrush_t{static_cast<int32_t>(map.bsp.dbrushsides.size()), 0, b.contents.native}); dbrush_t{static_cast<int32_t>(map.bsp.dbrushsides.size()), 0, b->contents.native});
auto bevels = AddBrushBevels(b); auto bevels = AddBrushBevels(*b);
for (auto &plane : bevels) { for (auto &plane : bevels) {
map.bsp.dbrushsides.push_back( 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"); logging::print(logging::flag::PROGRESS, "---- Brush_LoadEntity ----\n");
auto stats = Brush_LoadEntity(entity, hullnum); 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); entity->brushes = ChopBrushes(entity->brushes);
if (entity == map.world_entity() && hullnum == 0) { 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 hull sizes
*/ */
static void BSPX_Brushes_AddModel(struct bspxbrushes_s *ctx, int modelnum, std::vector<brush_t> &brushes) static void BSPX_Brushes_AddModel(
struct bspxbrushes_s *ctx, int modelnum, std::vector<std::unique_ptr<brush_t>> &brushes)
{ {
bspxbrushes_permodel permodel{1, modelnum}; bspxbrushes_permodel permodel{1, modelnum};
for (auto &b : brushes) { for (auto &b : brushes) {
permodel.numbrushes++; permodel.numbrushes++;
for (auto &f : b.faces) { for (auto &f : b->faces) {
/*skip axial*/ /*skip axial*/
if (fabs(map.planes[f.planenum].normal[0]) == 1 || fabs(map.planes[f.planenum].normal[1]) == 1 || 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) 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) { for (auto &b : brushes) {
bspxbrushes_perbrush perbrush{}; bspxbrushes_perbrush perbrush{};
for (auto &f : b.faces) { for (auto &f : b->faces) {
/*skip axial*/ /*skip axial*/
if (fabs(map.planes[f.planenum].normal[0]) == 1 || fabs(map.planes[f.planenum].normal[1]) == 1 || 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) 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.numfaces++;
} }
perbrush.bounds = b.bounds; perbrush.bounds = b->bounds;
switch (b.contents.native) { switch (b->contents.native) {
// contents should match the engine. // contents should match the engine.
case CONTENTS_EMPTY: // really an error, but whatever case CONTENTS_EMPTY: // really an error, but whatever
case CONTENTS_SOLID: // these are okay 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_SLIME:
case CONTENTS_LAVA: case CONTENTS_LAVA:
case CONTENTS_SKY: case CONTENTS_SKY:
if (b.contents.is_clip()) { if (b->contents.is_clip()) {
perbrush.contents = -8; perbrush.contents = -8;
} else { } else {
perbrush.contents = b.contents.native; perbrush.contents = b->contents.native;
} }
break; break;
// case CONTENTS_LADDER: // case CONTENTS_LADDER:
// perbrush.contents = -16; // perbrush.contents = -16;
// break; // break;
default: { default: {
if (b.contents.is_clip()) { if (b->contents.is_clip()) {
perbrush.contents = -8; perbrush.contents = -8;
} else { } else {
logging::print("WARNING: Unknown contents: {}. Translating to solid.\n", 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; perbrush.contents = CONTENTS_SOLID;
} }
break; break;
@ -948,7 +954,7 @@ static void BSPX_Brushes_AddModel(struct bspxbrushes_s *ctx, int modelnum, std::
str <= perbrush; str <= perbrush;
for (auto &f : b.faces) { for (auto &f : b->faces) {
/*skip axial*/ /*skip axial*/
if (fabs(map.planes[f.planenum].normal[0]) == 1 || fabs(map.planes[f.planenum].normal[1]) == 1 || 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) fabs(map.planes[f.planenum].normal[2]) == 1)

View File

@ -297,7 +297,7 @@ ChooseMidPlaneFromList
The clipping hull BSP doesn't worry about avoiding splits The clipping hull BSP doesn't worry about avoiding splits
================== ==================
*/ */
static face_t *ChooseMidPlaneFromList(std::vector<brush_t> &brushes, const aabb3d &bounds) static face_t *ChooseMidPlaneFromList(std::vector<std::unique_ptr<brush_t>> &brushes, const aabb3d &bounds)
{ {
/* pick the plane that splits the least */ /* pick the plane that splits the least */
vec_t bestaxialmetric = VECT_MAX; vec_t bestaxialmetric = VECT_MAX;
@ -307,11 +307,11 @@ static face_t *ChooseMidPlaneFromList(std::vector<brush_t> &brushes, const aabb3
for (int pass = 0; pass < 2; pass++) { for (int pass = 0; pass < 2; pass++) {
for (auto &brush : brushes) { for (auto &brush : brushes) {
if (brush.contents.is_detail() != (pass == 1)) { if (brush->contents.is_detail() != (pass == 1)) {
continue; continue;
} }
for (auto &face : brush.faces) { for (auto &face : brush->faces) {
if (face.onnode) if (face.onnode)
continue; continue;
if (!face.visible) { if (!face.visible) {
@ -363,7 +363,7 @@ The real BSP heuristic
fixme-brushbsp: prefer splits that include a lot of brush sides? fixme-brushbsp: prefer splits that include a lot of brush sides?
================== ==================
*/ */
static face_t *ChoosePlaneFromList(std::vector<brush_t> &brushes, const aabb3d &bounds) static face_t *ChoosePlaneFromList(std::vector<std::unique_ptr<brush_t>> &brushes, const aabb3d &bounds)
{ {
/* pick the plane that splits the least */ /* pick the plane that splits the least */
int minsplits = INT_MAX - 1; int minsplits = INT_MAX - 1;
@ -373,11 +373,11 @@ static face_t *ChoosePlaneFromList(std::vector<brush_t> &brushes, const aabb3d &
/* Two passes - exhaust all non-detail faces before details */ /* Two passes - exhaust all non-detail faces before details */
for (int pass = 0; pass < 2; pass++) { for (int pass = 0; pass < 2; pass++) {
for (auto &brush : brushes) { for (auto &brush : brushes) {
if (brush.contents.is_detail() != (pass == 1)) { if (brush->contents.is_detail() != (pass == 1)) {
continue; continue;
} }
for (auto &face : brush.faces) { for (auto &face : brush->faces) {
if (face.onnode) { if (face.onnode) {
continue; continue;
} }
@ -394,7 +394,7 @@ static face_t *ChoosePlaneFromList(std::vector<brush_t> &brushes, const aabb3d &
// now check all of the other faces in `brushes` and count how many // now check all of the other faces in `brushes` and count how many
// would get split if we used `face` as the splitting plane // would get split if we used `face` as the splitting plane
for (auto &brush2 : brushes) { for (auto &brush2 : brushes) {
for (auto &face2 : brush2.faces) { for (auto &face2 : brush2->faces) {
if (face2.planenum == face.planenum || face2.onnode) if (face2.planenum == face.planenum || face2.onnode)
continue; continue;
if (!face2.visible) if (!face2.visible)
@ -458,13 +458,13 @@ returns NULL if the surface list can not be divided any more (a leaf)
Called in parallel. Called in parallel.
================== ==================
*/ */
static face_t *SelectPartition(std::vector<brush_t> &brushes) static face_t *SelectPartition(std::vector<std::unique_ptr<brush_t>> &brushes)
{ {
// calculate a bounding box of the entire surfaceset // calculate a bounding box of the entire surfaceset
aabb3d bounds; aabb3d bounds;
for (auto &brush : brushes) { for (auto &brush : brushes) {
bounds += brush.bounds; bounds += brush->bounds;
} }
// how much of the map are we partitioning? // how much of the map are we partitioning?
@ -610,20 +610,20 @@ vec_t BrushVolume(const brush_t &brush)
================ ================
SplitBrush SplitBrush
Generates two new brushes, leaving the original Note, it's useful to take/return std::unique_ptr so it can quickly return the
unchanged input.
https://github.com/id-Software/Quake-2-Tools/blob/master/bsp/qbsp3/brushbsp.c#L935 https://github.com/id-Software/Quake-2-Tools/blob/master/bsp/qbsp3/brushbsp.c#L935
================ ================
*/ */
twosided<std::optional<brush_t>> SplitBrush(const brush_t &brush, const qplane3d &split) twosided<std::unique_ptr<brush_t>> SplitBrush(std::unique_ptr<brush_t> brush, const qplane3d &split)
{ {
twosided<std::optional<brush_t>> result; twosided<std::unique_ptr<brush_t>> result;
// check all points // check all points
vec_t d_front = 0; vec_t d_front = 0;
vec_t d_back = 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++) { for (int j = 0; j < face.w.size(); j++) {
vec_t d = qv::dot(face.w[j], split.normal) - split.dist; vec_t d = qv::dot(face.w[j], split.normal) - split.dist;
if (d > 0 && d > d_front) if (d > 0 && d > d_front)
@ -634,18 +634,18 @@ twosided<std::optional<brush_t>> SplitBrush(const brush_t &brush, const qplane3d
} }
if (d_front < 0.1) // PLANESIDE_EPSILON) if (d_front < 0.1) // PLANESIDE_EPSILON)
{ // only on back { // only on back
result.back = {brush}; result.back = std::move(brush);
return result; return result;
} }
if (d_back > -0.1) // PLANESIDE_EPSILON) if (d_back > -0.1) // PLANESIDE_EPSILON)
{ // only on front { // only on front
result.front = {brush}; result.front = std::move(brush);
return result; return result;
} }
// create a new winding from the split plane // create a new winding from the split plane
auto w = std::optional<winding_t>{BaseWindingForPlane(split)}; auto w = std::optional<winding_t>{BaseWindingForPlane(split)};
for (auto &face : brush.faces) { for (auto &face : brush->faces) {
if (!w) { if (!w) {
break; break;
} }
@ -654,11 +654,11 @@ twosided<std::optional<brush_t>> SplitBrush(const brush_t &brush, const qplane3d
} }
if (!w || WindingIsTiny(*w)) { // the brush isn't really split 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) if (side == SIDE_FRONT)
result.front = {brush}; result.front = std::move(brush);
else else
result.back = {brush}; result.back = std::move(brush);
return result; return result;
} }
@ -673,16 +673,16 @@ twosided<std::optional<brush_t>> SplitBrush(const brush_t &brush, const qplane3d
// start with 2 empty brushes // start with 2 empty brushes
for (int i = 0; i < 2; i++) { for (int i = 0; i < 2; i++) {
result[i] = { brush_t{} }; result[i] = std::make_unique<brush_t>();
result[i]->original = brush.original; result[i]->original = brush->original;
// fixme-brushbsp: add a brush_t copy constructor to make sure we get all fields // fixme-brushbsp: add a brush_t copy constructor to make sure we get all fields
result[i]->contents = brush.contents; result[i]->contents = brush->contents;
result[i]->lmshift = brush.lmshift; result[i]->lmshift = brush->lmshift;
} }
// split all the current windings // 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*/); auto cw = face.w.clip(split, 0 /*PLANESIDE_EPSILON*/);
for (size_t j = 0; j < 2; j++) { for (size_t j = 0; j < 2; j++) {
if (!cw[j]) if (!cw[j])
@ -721,7 +721,7 @@ twosided<std::optional<brush_t>> SplitBrush(const brush_t &brush, const qplane3d
} }
if (result[i]->faces.size() < 3 || bogus) { if (result[i]->faces.size() < 3 || bogus) {
result[i] = std::nullopt; result[i] = nullptr;
} }
} }
@ -731,10 +731,10 @@ twosided<std::optional<brush_t>> SplitBrush(const brush_t &brush, const qplane3d
else else
logging::print("split not on both sides\n"); logging::print("split not on both sides\n");
if (result[0]) { if (result[0]) {
result.front = {brush}; result.front = std::move(brush);
} }
if (result[1]) { if (result[1]) {
result.back = {brush}; result.back = std::move(brush);
} }
return result; return result;
} }
@ -764,7 +764,7 @@ twosided<std::optional<brush_t>> SplitBrush(const brush_t &brush, const qplane3d
for (i = 0; i < 2; i++) { for (i = 0; i < 2; i++) {
v1 = BrushVolume(*result[i]); v1 = BrushVolume(*result[i]);
if (v1 < 1.0) { if (v1 < 1.0) {
result[i] = std::nullopt; result[i] = nullptr;
// qprintf ("tiny volume after clip\n"); // 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); DivideBounds(node->bounds, split, node->children[0]->bounds, node->children[1]->bounds);
} }
static bool AllDetail(const std::vector<brush_t> &brushes) static bool AllDetail(const std::vector<std::unique_ptr<brush_t>> &brushes)
{ {
for (auto &brush : brushes) { for (auto &brush : brushes) {
if (!brush.contents.is_detail()) { if (!brush->contents.is_detail()) {
return false; return false;
} }
} }
@ -818,18 +818,18 @@ original faces that have some fragment inside this leaf.
Called in parallel. Called in parallel.
================== ==================
*/ */
static void CreateLeaf(const std::vector<brush_t> &brushes, node_t *leafnode) static void CreateLeaf(std::vector<std::unique_ptr<brush_t>> brushes, node_t *leafnode)
{ {
leafnode->facelist.clear(); leafnode->facelist.clear();
leafnode->planenum = PLANENUM_LEAF; leafnode->planenum = PLANENUM_LEAF;
leafnode->contents = options.target_game->create_empty_contents(); leafnode->contents = options.target_game->create_empty_contents();
for (auto &brush : brushes) { for (auto &brush : brushes) {
leafnode->contents = MergeContents(leafnode->contents, brush.contents); leafnode->contents = MergeContents(leafnode->contents, brush->contents);
} }
for (auto &brush : brushes) { for (auto &brush : brushes) {
Q_assert(brush.original != nullptr); Q_assert(brush->original != nullptr);
leafnode->original_brushes.push_back(brush.original); leafnode->original_brushes.push_back(brush->original);
} }
if (leafnode->contents.extended & CFLAGS_ILLUSIONARY_VISBLOCKER) { if (leafnode->contents.extended & CFLAGS_ILLUSIONARY_VISBLOCKER) {
@ -859,14 +859,14 @@ PartitionBrushes
Called in parallel. Called in parallel.
================== ==================
*/ */
static void PartitionBrushes(std::vector<brush_t> brushes, node_t *node) static void PartitionBrushes(std::vector<std::unique_ptr<brush_t>> brushes, node_t *node)
{ {
face_t *split = SelectPartition(brushes); face_t *split = SelectPartition(brushes);
if (split == nullptr) { // this is a leaf node if (split == nullptr) { // this is a leaf node
node->planenum = PLANENUM_LEAF; node->planenum = PLANENUM_LEAF;
CreateLeaf(brushes, node); CreateLeaf(std::move(brushes), node);
return; return;
} }
@ -882,10 +882,11 @@ static void PartitionBrushes(std::vector<brush_t> brushes, node_t *node)
DivideNodeBounds(node, splitplane); DivideNodeBounds(node, splitplane);
// multiple surfaces, so split all the polysurfaces into front and back lists // multiple surfaces, so split all the polysurfaces into front and back lists
std::vector<brush_t> frontlist, backlist; std::vector<std::unique_ptr<brush_t>> frontlist, backlist;
for (auto &brush : brushes) { 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 // mark faces which were used as a splitter
for (auto &brushMaybe : frags) { for (auto &brushMaybe : frags) {
@ -902,13 +903,13 @@ static void PartitionBrushes(std::vector<brush_t> brushes, node_t *node)
if (frags.front->faces.empty()) { if (frags.front->faces.empty()) {
FError("Surface with no faces"); 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) {
if (frags.back->faces.empty()) { if (frags.back->faces.empty()) {
FError("Surface with no faces"); 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 visible_brush_sides = 0;
int invisible_brush_sides = 0; int invisible_brush_sides = 0;
for (const auto &brush : entity->brushes) { for (const auto &brush : entity->brushes) {
for (auto &side : brush.faces) { for (auto &side : brush->faces) {
if (side.visible) { if (side.visible) {
++visible_brush_sides; ++visible_brush_sides;
} else { } else {
@ -989,10 +990,10 @@ node_t *SolidBSP(mapentity_t *entity, bool midsplit)
mapbrushes = entity->brushes.size(); mapbrushes = entity->brushes.size();
// set the original pointers // set the original pointers
std::vector<brush_t> brushcopies; std::vector<std::unique_ptr<brush_t>> brushcopies;
for (brush_t &original : entity->brushes) { for (const auto &original : entity->brushes) {
brush_t copy = original; auto copy = std::make_unique<brush_t>(*original);
copy.original = &original; copy->original = original.get();
brushcopies.push_back(std::move(copy)); brushcopies.push_back(std::move(copy));
} }
PartitionBrushes(std::move(brushcopies), headnode); PartitionBrushes(std::move(brushcopies), headnode);

View File

@ -702,13 +702,13 @@ void MakeVisibleFaces(mapentity_t* entity, node_t* headnode)
c_nodefaces = 0; c_nodefaces = 0;
for (auto &brush : entity->brushes) { for (auto &brush : entity->brushes) {
for (auto &face : brush.faces) { for (auto &face : brush->faces) {
if (!face.visible) { if (!face.visible) {
continue; continue;
} }
face_t *temp = CopyFace(&face); face_t *temp = CopyFace(&face);
AddFaceToTree_r(entity, temp, &brush, headnode); AddFaceToTree_r(entity, temp, brush.get(), headnode);
} }
} }

View File

@ -1,6 +1,7 @@
#include <gmock/gmock.h> #include <gmock/gmock.h>
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include <qbsp/brush.hh>
#include <qbsp/qbsp.hh> #include <qbsp/qbsp.hh>
#include <qbsp/map.hh> #include <qbsp/map.hh>
#include <common/fs.hh> #include <common/fs.hh>