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.
*/
brush_t *original;
uint32_t file_order;
aabb3d bounds;
std::vector<face_t> faces;
contentflags_t contents; /* BSP contents */

View File

@ -21,14 +21,13 @@
#pragma once
#include <qbsp/brush.hh>
#include <common/qvec.hh>
#include <list>
#include <tuple>
#include <vector>
struct brush_t;
struct face_t;
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);
void UpdateFaceSphere(face_t *in);
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
#include <qbsp/brush.hh>
#include <qbsp/qbsp.hh>
#include <common/bspfile.hh>
#include <common/parser.hh>
#include "common/cmdlib.hh"
@ -32,6 +33,8 @@
#include <unordered_map>
#include <list>
struct brush_t;
struct qbsp_plane_t : qplane3d
{
int type = 0;
@ -96,7 +99,7 @@ public:
std::vector<std::pair<std::string, std::string>> epairs;
aabb3d bounds;
std::vector<brush_t> brushes;
std::vector<std::unique_ptr<brush_t>> brushes;
int firstoutputfacenumber = -1;
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_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);

View File

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

View File

@ -21,21 +21,21 @@
#pragma once
#include <qbsp/brush.hh>
#include <common/qvec.hh>
#include <atomic>
#include <list>
#include <optional>
#include <memory>
extern std::atomic<int> splitnodes;
struct brush_t;
struct node_t;
struct face_t;
class mapentity_t;
void DetailToSolid(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);

View File

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

View File

@ -215,21 +215,11 @@ static void FreeFaces(std::list<face_t *> &facelist)
//==========================================================================
/*
==================
BrushIndexInMap
Returns the index of the brush in the .map files.
Only call with an "original" brush (from entity->brushes).
Used for clipping priority.
==================
*/
static int BrushIndexInMap(const mapentity_t *entity, const brush_t *brush)
static std::vector<std::unique_ptr<brush_t>> SingleBrush(std::unique_ptr<brush_t> a)
{
Q_assert(brush >= entity->brushes.data());
Q_assert(brush < (entity->brushes.data() + entity->brushes.size()));
return static_cast<int>(brush - entity->brushes.data());
std::vector<std::unique_ptr<brush_t>> res;
res.push_back(std::move(a));
return res;
}
/*
@ -239,30 +229,46 @@ SubtractBrush
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
for (const auto &side : b.faces) {
auto [front, back] = SplitBrush(a, Face_Plane(&side));
if (front && !back) {
// `a` is fully in front of this side of b, so they don't actually intersect
return {a};
// is `a` fully in front of `side`?
bool fully_infront = true;
// fixme-brushbsp: factor this out somewhere
for (const auto &a_face : a->faces) {
for (const auto &a_point : a_face.w) {
if (Face_Plane(&side).distance_to(a_point) < 0) {
fully_infront = false;
break;
}
}
if (!fully_infront) {
break;
}
}
std::vector<brush_t> frontlist;
std::vector<brush_t> unclassified{a};
if (fully_infront) {
// `a` is fully in front of this side of b, so they don't actually intersect
return SingleBrush(std::move(a));
}
}
std::vector<std::unique_ptr<brush_t>> frontlist;
std::vector<std::unique_ptr<brush_t>> unclassified = SingleBrush(std::move(a));
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) {
auto [front, back] = SplitBrush(fragment, Face_Plane(&side));
for (auto &fragment : unclassified) {
// destructively processing `unclassified` here
auto [front, back] = SplitBrush(std::move(fragment), Face_Plane(&side));
if (front) {
frontlist.push_back(*front);
frontlist.push_back(std::move(front));
}
if (back) {
new_unclassified.push_back(*back);
new_unclassified.push_back(std::move(back));
}
}
@ -284,7 +290,7 @@ bool BrushGE(const brush_t& a, const brush_t& b)
// same contents clip each other
if (a.contents == b.contents && a.contents.clips_same_type()) {
// map file order
return &a > &b;
return a.file_order > b.file_order;
}
// only chop if at least one of the two contents is
@ -298,7 +304,7 @@ bool BrushGE(const brush_t& a, const brush_t& b)
if (a_pri == b_pri) {
// map file order
return &a > &b;
return a.file_order > b.file_order;
}
return a_pri >= b_pri;
@ -311,12 +317,13 @@ ChopBrushes
Clips off any overlapping portions of brushes
==================
*/
std::vector<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__);
// output vector for the parallel_for
std::vector<std::vector<brush_t>> brush_fragments;
// each inner vector corresponds to a brush in `input`
// (set up this way for thread safety)
std::vector<std::vector<std::unique_ptr<brush_t>>> brush_fragments;
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"
*/
tbb::parallel_for(static_cast<size_t>(0), input.size(), [input, &brush_fragments](const size_t i) {
const auto &brush = input[i];
tbb::parallel_for(static_cast<size_t>(0), input.size(), [&](const size_t i) {
const auto& brush = input[i];
// 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) {
if (&brush == &clipbrush) {
if (brush == clipbrush) {
continue;
}
if (brush.bounds.disjoint(clipbrush.bounds)) {
if (brush->bounds.disjoint(clipbrush->bounds)) {
continue;
}
if (BrushGE(clipbrush, brush)) {
std::vector<brush_t> new_result;
if (BrushGE(*clipbrush, *brush)) {
std::vector<std::unique_ptr<brush_t>> new_result;
// clipbrush is stronger. clip all existing fragments to clipbrush
for (const auto &current_fragment : brush_result) {
for (const auto &new_fragment : SubtractBrush(current_fragment, clipbrush)) {
new_result.push_back(new_fragment);
// clipbrush is stronger.
// rebuild existing fragments in brush_result, cliping them to clipbrush
for (auto &current_fragment : brush_result) {
for (auto &new_fragment : SubtractBrush(std::move(current_fragment), *clipbrush)) {
new_result.push_back(std::move(new_fragment));
}
}
@ -355,11 +367,11 @@ std::vector<brush_t> ChopBrushes(const std::vector<brush_t>& input)
}
// save the result
brush_fragments[i] = brush_result;
brush_fragments[i] = std::move(brush_result);
});
// 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 : fragment_list) {
result.push_back(std::move(fragment));

View File

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

View File

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

View File

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

View File

@ -349,12 +349,12 @@ static void ExportBrushList(mapentity_t *entity, node_t *node)
brush_state = {};
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{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) {
map.bsp.dbrushsides.push_back(
@ -654,6 +654,11 @@ static void ProcessEntity(mapentity_t *entity, const int hullnum)
logging::print(logging::flag::PROGRESS, "---- Brush_LoadEntity ----\n");
auto stats = Brush_LoadEntity(entity, hullnum);
// assign brush file order
for (size_t i = 0; i < entity->brushes.size(); ++i) {
entity->brushes[i]->file_order = i;
}
entity->brushes = ChopBrushes(entity->brushes);
if (entity == map.world_entity() && hullnum == 0) {
@ -880,13 +885,14 @@ Generates a submodel's direct brush information to a separate file, so the engin
hull sizes
*/
static void BSPX_Brushes_AddModel(struct bspxbrushes_s *ctx, int modelnum, std::vector<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};
for (auto &b : brushes) {
permodel.numbrushes++;
for (auto &f : b.faces) {
for (auto &f : b->faces) {
/*skip axial*/
if (fabs(map.planes[f.planenum].normal[0]) == 1 || fabs(map.planes[f.planenum].normal[1]) == 1 ||
fabs(map.planes[f.planenum].normal[2]) == 1)
@ -907,7 +913,7 @@ static void BSPX_Brushes_AddModel(struct bspxbrushes_s *ctx, int modelnum, std::
for (auto &b : brushes) {
bspxbrushes_perbrush perbrush{};
for (auto &f : b.faces) {
for (auto &f : b->faces) {
/*skip axial*/
if (fabs(map.planes[f.planenum].normal[0]) == 1 || fabs(map.planes[f.planenum].normal[1]) == 1 ||
fabs(map.planes[f.planenum].normal[2]) == 1)
@ -915,9 +921,9 @@ static void BSPX_Brushes_AddModel(struct bspxbrushes_s *ctx, int modelnum, std::
perbrush.numfaces++;
}
perbrush.bounds = b.bounds;
perbrush.bounds = b->bounds;
switch (b.contents.native) {
switch (b->contents.native) {
// contents should match the engine.
case CONTENTS_EMPTY: // really an error, but whatever
case CONTENTS_SOLID: // these are okay
@ -925,21 +931,21 @@ static void BSPX_Brushes_AddModel(struct bspxbrushes_s *ctx, int modelnum, std::
case CONTENTS_SLIME:
case CONTENTS_LAVA:
case CONTENTS_SKY:
if (b.contents.is_clip()) {
if (b->contents.is_clip()) {
perbrush.contents = -8;
} else {
perbrush.contents = b.contents.native;
perbrush.contents = b->contents.native;
}
break;
// case CONTENTS_LADDER:
// perbrush.contents = -16;
// break;
default: {
if (b.contents.is_clip()) {
if (b->contents.is_clip()) {
perbrush.contents = -8;
} else {
logging::print("WARNING: Unknown contents: {}. Translating to solid.\n",
b.contents.to_string(options.target_game));
b->contents.to_string(options.target_game));
perbrush.contents = CONTENTS_SOLID;
}
break;
@ -948,7 +954,7 @@ static void BSPX_Brushes_AddModel(struct bspxbrushes_s *ctx, int modelnum, std::
str <= perbrush;
for (auto &f : b.faces) {
for (auto &f : b->faces) {
/*skip axial*/
if (fabs(map.planes[f.planenum].normal[0]) == 1 || fabs(map.planes[f.planenum].normal[1]) == 1 ||
fabs(map.planes[f.planenum].normal[2]) == 1)

View File

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

View File

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

View File

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