remove bspbrush_t::original; it is no longer required since mapbrush_t is what the output is being written to

use some explicit types (bspbrush_t::ptr for pointer, bspbrush_t::container for container of pointers, and bspbrush_t::make_ptr to create one) so that we can swap it out with different pointers easier later
swap out bspbrush_t::ptr from unique_ptr to shared_ptr; it's not important yet, but it does not increase compilation time and will allow us to pass things to functions like SplitBrush without destroying the original as long as we still have a reference to it somewhere (important for ChopBrushes)
add get_positive_plane to mapface_t
instead of creating a whole copy of the bspbrush_t list, BrushBSP now just creates copies of the shared_ptrs which are then filtered down the tree; the originals are never modified by SplitBrushList and friends, only split into new brushes, so this is safe from what I can tell
portals now directly reference the mapface_ts
original_brushes now directly reference the mapbrush_ts
early exit in `BrushBSP` if brush list is empty (moved to top)
This commit is contained in:
Jonathan 2022-08-10 16:02:16 -04:00
parent faf9173f10
commit 097b564cc6
16 changed files with 114 additions and 126 deletions

View File

@ -22,13 +22,13 @@
#pragma once #pragma once
#include <qbsp/winding.hh> #include <qbsp/winding.hh>
#include <qbsp/qbsp.hh>
#include <common/aabb.hh> #include <common/aabb.hh>
#include <optional> #include <optional>
class mapentity_t; class mapentity_t;
struct maptexinfo_t; struct maptexinfo_t;
struct mapface_t; struct mapface_t;
struct qbsp_plane_t;
struct side_t struct side_t
{ {
@ -51,11 +51,19 @@ class mapbrush_t;
struct bspbrush_t struct bspbrush_t
{ {
using ptr = std::shared_ptr<bspbrush_t>;
using container = std::vector<ptr>;
template<typename... Args>
static inline ptr make_ptr(Args&& ...args)
{
return std::make_shared<bspbrush_t>(std::forward<Args>(args)...);
}
/** /**
* The brushes in main brush vectors are considered originals. Brush fragments created during * The brushes in main brush vectors are considered originals. Brush fragments created during
* the BrushBSP will have this pointing back to the original brush in the list. * the BrushBSP will have this pointing back to the original brush in the list.
*/ */
bspbrush_t *original;
mapbrush_t *mapbrush; mapbrush_t *mapbrush;
aabb3d bounds; aabb3d bounds;
int side, testside; // side of node during construction int side, testside; // side of node during construction
@ -67,10 +75,8 @@ struct bspbrush_t
bool update_bounds(bool warn_on_failures); bool update_bounds(bool warn_on_failures);
std::unique_ptr<bspbrush_t> copy_unique() const; ptr copy_unique() const;
}; };
using bspbrush_vector_t = std::vector<std::unique_ptr<bspbrush_t>>;
std::optional<bspbrush_t> LoadBrush(const mapentity_t *src, mapbrush_t *mapbrush, const contentflags_t &contents, const int hullnum); std::optional<bspbrush_t> LoadBrush(const mapentity_t *src, mapbrush_t *mapbrush, const contentflags_t &contents, const int hullnum);
bool CreateBrushWindings(bspbrush_t *brush); bool CreateBrushWindings(bspbrush_t *brush);

View File

@ -39,5 +39,5 @@ struct tree_t;
constexpr vec_t EDGE_LENGTH_EPSILON = 0.2; constexpr vec_t EDGE_LENGTH_EPSILON = 0.2;
bool WindingIsTiny(const winding_t &w, double size = EDGE_LENGTH_EPSILON); bool WindingIsTiny(const winding_t &w, double size = EDGE_LENGTH_EPSILON);
std::unique_ptr<bspbrush_t> BrushFromBounds(const aabb3d &bounds); bspbrush_t::ptr BrushFromBounds(const aabb3d &bounds);
std::unique_ptr<tree_t> BrushBSP(mapentity_t *entity, const bspbrush_vector_t &brushes, std::optional<bool> forced_quick_tree); std::unique_ptr<tree_t> BrushBSP(mapentity_t *entity, const bspbrush_t::container &brushes, std::optional<bool> forced_quick_tree);

View File

@ -37,4 +37,3 @@ std::unique_ptr<face_t> CopyFace(const face_t *in);
std::tuple<std::unique_ptr<face_t>, std::unique_ptr<face_t>> SplitFace( std::tuple<std::unique_ptr<face_t>, std::unique_ptr<face_t>> SplitFace(
std::unique_ptr<face_t> in, const qplane3d &split); std::unique_ptr<face_t> in, const qplane3d &split);
void UpdateFaceSphere(face_t *in); void UpdateFaceSphere(face_t *in);
bspbrush_vector_t MakeBspBrushList(const bspbrush_vector_t &brushes);

View File

@ -69,6 +69,7 @@ struct mapface_t
void set_texvecs(const texvecf &vecs); void set_texvecs(const texvecf &vecs);
const qbsp_plane_t &get_plane() const; const qbsp_plane_t &get_plane() const;
const qbsp_plane_t &get_positive_plane() const;
}; };
enum class brushformat_t enum class brushformat_t
@ -344,7 +345,7 @@ qvec3d FixRotateOrigin(mapentity_t *entity);
constexpr int HULL_COLLISION = -1; constexpr int HULL_COLLISION = -1;
/* Create BSP brushes from map brushes */ /* Create BSP brushes from map brushes */
void Brush_LoadEntity(mapentity_t *entity, const int hullnum, bspbrush_vector_t &brushes); void Brush_LoadEntity(mapentity_t *entity, const int hullnum, bspbrush_t::container &brushes);
std::list<face_t *> CSGFace( std::list<face_t *> CSGFace(
face_t *srcface, const mapentity_t *srcentity, const bspbrush_t *srcbrush, const node_t *srcnode); face_t *srcface, const mapentity_t *srcentity, const bspbrush_t *srcbrush, const node_t *srcnode);
@ -366,6 +367,6 @@ void ExportObj_Brushes(const std::string &filesuffix, const std::vector<const bs
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<std::unique_ptr<bspbrush_t>> &list); void WriteBspBrushMap(const fs::path &name, const bspbrush_t::container &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

@ -28,7 +28,7 @@ class mapentity_t;
struct node_t; struct node_t;
struct tree_t; struct tree_t;
bool FillOutside(mapentity_t *entity, tree_t *tree, const int hullnum, bspbrush_vector_t &brushes); bool FillOutside(mapentity_t *entity, tree_t *tree, const int hullnum, bspbrush_t::container &brushes);
std::vector<node_t *> FindOccupiedClusters(node_t *headnode); std::vector<node_t *> FindOccupiedClusters(node_t *headnode);
void FillBrushEntity(mapentity_t *entity, tree_t *tree, const int hullnum, bspbrush_vector_t &brushes); void FillBrushEntity(mapentity_t *entity, tree_t *tree, const int hullnum, bspbrush_t::container &brushes);

View File

@ -40,8 +40,8 @@ struct portal_t
std::unique_ptr<winding_t> winding; std::unique_ptr<winding_t> winding;
bool sidefound; // false if ->side hasn't been checked bool sidefound; // false if ->side hasn't been checked
side_t *sides[2]; // [0] = the brush side visible on nodes[0] - it could come from a brush in nodes[1]. NULL = mapface_t *sides[2]; // [0] = the brush side visible on nodes[0] - it could come from a brush in nodes[1]. NULL =
// non-visible // non-visible
face_t *face[2]; // output face in bsp file face_t *face[2]; // output face in bsp file
}; };
@ -78,4 +78,4 @@ std::list<std::unique_ptr<buildportal_t>> MakeHeadnodePortals(tree_t *tree);
void MakePortalsFromBuildportals(tree_t *tree, std::list<std::unique_ptr<buildportal_t>> buildportals); void MakePortalsFromBuildportals(tree_t *tree, std::list<std::unique_ptr<buildportal_t>> buildportals);
void EmitAreaPortals(node_t *headnode); void EmitAreaPortals(node_t *headnode);
void FloodAreas(mapentity_t *entity, node_t *headnode); void FloodAreas(mapentity_t *entity, node_t *headnode);
void MarkVisibleSides(tree_t *tree, mapentity_t *entity, bspbrush_vector_t &brushes); void MarkVisibleSides(tree_t *tree, mapentity_t *entity, bspbrush_t::container &brushes);

View File

@ -48,6 +48,7 @@
#include <common/aabb.hh> #include <common/aabb.hh>
#include <common/settings.hh> #include <common/settings.hh>
#include <common/fs.hh> #include <common/fs.hh>
#include <qbsp/brush.hh>
enum texcoord_style_t enum texcoord_style_t
{ {
@ -633,7 +634,7 @@ struct node_t
aabb3d bounds; // bounding volume, not just points inside aabb3d bounds; // bounding volume, not just points inside
node_t *parent; node_t *parent;
// this is also a bounding volume like `bounds` // this is also a bounding volume like `bounds`
std::unique_ptr<bspbrush_t> volume; // one for each leaf/node bspbrush_t::ptr volume; // one for each leaf/node
bool is_leaf = false; bool is_leaf = false;
// information for decision nodes // information for decision nodes
@ -662,7 +663,7 @@ struct node_t
uint32_t firstleafbrush; // Q2 uint32_t firstleafbrush; // Q2
uint32_t numleafbrushes; uint32_t numleafbrushes;
int32_t area; int32_t area;
std::vector<bspbrush_t *> original_brushes; std::vector<mapbrush_t *> original_brushes;
}; };
void InitQBSP(int argc, const char **argv); void InitQBSP(int argc, const char **argv);

View File

@ -40,14 +40,12 @@ const qbsp_plane_t &side_t::get_plane() const
const qbsp_plane_t &side_t::get_positive_plane() const const qbsp_plane_t &side_t::get_positive_plane() const
{ {
auto &p = map.get_plane(planenum & ~1); return map.get_plane(planenum & ~1);
Q_assert(p.get_normal()[(int) p.get_type() % 3] > 0);
return p;
} }
std::unique_ptr<bspbrush_t> bspbrush_t::copy_unique() const bspbrush_t::ptr bspbrush_t::copy_unique() const
{ {
return std::make_unique<bspbrush_t>(*this); return bspbrush_t::make_ptr(*this);
} }
/* /*
@ -347,7 +345,7 @@ std::optional<bspbrush_t> LoadBrush(const mapentity_t *src, mapbrush_t *mapbrush
//============================================================================= //=============================================================================
static void Brush_LoadEntity(mapentity_t *dst, mapentity_t *src, const int hullnum, content_stats_base_t &stats, bspbrush_vector_t &brushes) static void Brush_LoadEntity(mapentity_t *dst, mapentity_t *src, const int hullnum, content_stats_base_t &stats, bspbrush_t::container &brushes)
{ {
// _omitbrushes 1 just discards all brushes in the entity. // _omitbrushes 1 just discards all brushes in the entity.
// could be useful for geometry guides, selective compilation, etc. // could be useful for geometry guides, selective compilation, etc.
@ -515,7 +513,7 @@ hullnum HULL_COLLISION should contain ALL brushes. (used by BSPX_CreateBrushList
hullnum 0 does not contain clip brushes. hullnum 0 does not contain clip brushes.
============ ============
*/ */
void Brush_LoadEntity(mapentity_t *entity, const int hullnum, bspbrush_vector_t &brushes) void Brush_LoadEntity(mapentity_t *entity, const int hullnum, bspbrush_t::container &brushes)
{ {
logging::funcheader(); logging::funcheader();

View File

@ -79,9 +79,9 @@ BrushFromBounds
Creates a new axial brush Creates a new axial brush
================== ==================
*/ */
std::unique_ptr<bspbrush_t> BrushFromBounds(const aabb3d &bounds) bspbrush_t::ptr BrushFromBounds(const aabb3d &bounds)
{ {
auto b = std::make_unique<bspbrush_t>(); auto b = bspbrush_t::make_ptr();
b->sides.resize(6); b->sides.resize(6);
for (int i = 0; i < 3; i++) { for (int i = 0; i < 3; i++) {
@ -377,7 +377,7 @@ Creates a leaf node.
Called in parallel. Called in parallel.
================== ==================
*/ */
static void LeafNode(node_t *leafnode, std::vector<std::unique_ptr<bspbrush_t>> brushes, bspstats_t &stats) static void LeafNode(node_t *leafnode, bspbrush_t::container brushes, bspstats_t &stats)
{ {
leafnode->facelist.clear(); leafnode->facelist.clear();
leafnode->is_leaf = true; leafnode->is_leaf = true;
@ -387,8 +387,8 @@ static void LeafNode(node_t *leafnode, std::vector<std::unique_ptr<bspbrush_t>>
leafnode->contents = qbsp_options.target_game->combine_contents(leafnode->contents, brush->contents); leafnode->contents = qbsp_options.target_game->combine_contents(leafnode->contents, brush->contents);
} }
for (auto &brush : brushes) { for (auto &brush : brushes) {
Q_assert(brush->original != nullptr); Q_assert(brush->mapbrush != nullptr);
leafnode->original_brushes.push_back(brush->original); leafnode->original_brushes.push_back(brush->mapbrush);
} }
qbsp_options.target_game->count_contents_in_stats(leafnode->contents, *stats.leafstats); qbsp_options.target_game->count_contents_in_stats(leafnode->contents, *stats.leafstats);
@ -434,10 +434,10 @@ 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
================ ================
*/ */
static twosided<std::unique_ptr<bspbrush_t>> SplitBrush(std::unique_ptr<bspbrush_t> brush, size_t planenum, bspstats_t &stats) static twosided<bspbrush_t::ptr> SplitBrush(bspbrush_t::ptr brush, size_t planenum, bspstats_t &stats)
{ {
const qplane3d &split = map.planes[planenum]; const qplane3d &split = map.planes[planenum];
twosided<std::unique_ptr<bspbrush_t>> result; twosided<bspbrush_t::ptr> result;
// check all points // check all points
vec_t d_front = 0; vec_t d_front = 0;
@ -493,7 +493,7 @@ static twosided<std::unique_ptr<bspbrush_t>> SplitBrush(std::unique_ptr<bspbrush
for (int i = 0; i < 2; i++) { for (int i = 0; i < 2; i++) {
result[i] = std::make_unique<bspbrush_t>(); result[i] = std::make_unique<bspbrush_t>();
result[i]->original = brush->original; result[i]->mapbrush = brush->mapbrush;
// fixme-brushbsp: add a bspbrush_t copy constructor to make sure we get all fields // fixme-brushbsp: add a bspbrush_t copy constructor to make sure we get all fields
result[i]->contents = brush->contents; result[i]->contents = brush->contents;
result[i]->sides.reserve(brush->sides.size() + 1); result[i]->sides.reserve(brush->sides.size() + 1);
@ -695,7 +695,7 @@ ChooseMidPlaneFromList
The clipping hull BSP doesn't worry about avoiding splits The clipping hull BSP doesn't worry about avoiding splits
================== ==================
*/ */
static std::optional<size_t> ChooseMidPlaneFromList(const std::vector<std::unique_ptr<bspbrush_t>> &brushes, const node_t *node, bspstats_t &stats) static std::optional<size_t> ChooseMidPlaneFromList(const bspbrush_t::container &brushes, const node_t *node, bspstats_t &stats)
{ {
vec_t bestaxialmetric = VECT_MAX; vec_t bestaxialmetric = VECT_MAX;
std::optional<size_t> bestaxialplane; std::optional<size_t> bestaxialplane;
@ -750,7 +750,7 @@ Using heuristics, chooses a plane to partition the brushes with.
Returns nullopt if there are no valid planes to split with. Returns nullopt if there are no valid planes to split with.
================ ================
*/ */
static std::optional<size_t> SelectSplitPlane(const std::vector<std::unique_ptr<bspbrush_t>> &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, std::optional<bool> forced_quick_tree, bspstats_t &stats)
{ {
// no brushes left to split, so we can't use any plane. // no brushes left to split, so we can't use any plane.
if (!brushes.size()) { if (!brushes.size()) {
@ -803,9 +803,9 @@ static std::optional<size_t> SelectSplitPlane(const std::vector<std::unique_ptr<
constexpr int numpasses = 4; constexpr int numpasses = 4;
for (int pass = 0; pass < numpasses; pass++) { for (int pass = 0; pass < numpasses; pass++) {
for (auto &brush : brushes) { for (auto &brush : brushes) {
if ((pass & 1) && !brush->original->contents.is_any_detail(qbsp_options.target_game)) if ((pass & 1) && !brush->mapbrush->contents.is_any_detail(qbsp_options.target_game))
continue; continue;
if (!(pass & 1) && brush->original->contents.is_any_detail(qbsp_options.target_game)) if (!(pass & 1) && brush->mapbrush->contents.is_any_detail(qbsp_options.target_game))
continue; continue;
for (auto &side : brush->sides) { for (auto &side : brush->sides) {
if (side.bevel) if (side.bevel)
@ -926,10 +926,10 @@ static std::optional<size_t> SelectSplitPlane(const std::vector<std::unique_ptr<
SplitBrushList SplitBrushList
================ ================
*/ */
static std::array<std::vector<std::unique_ptr<bspbrush_t>>, 2> SplitBrushList( static std::array<bspbrush_t::container, 2> SplitBrushList(
std::vector<std::unique_ptr<bspbrush_t>> brushes, size_t planenum, bspstats_t &stats) bspbrush_t::container brushes, size_t planenum, bspstats_t &stats)
{ {
std::array<std::vector<std::unique_ptr<bspbrush_t>>, 2> result; std::array<bspbrush_t::container, 2> result;
for (auto &brush : brushes) { for (auto &brush : brushes) {
int sides = brush->side; int sides = brush->side;
@ -979,7 +979,7 @@ BuildTree_r
Called in parallel. Called in parallel.
================== ==================
*/ */
static void BuildTree_r(tree_t *tree, node_t *node, std::vector<std::unique_ptr<bspbrush_t>> brushes, std::optional<bool> forced_quick_tree, bspstats_t &stats) static void BuildTree_r(tree_t *tree, node_t *node, bspbrush_t::container brushes, std::optional<bool> forced_quick_tree, bspstats_t &stats)
{ {
// find the best plane to use as a splitter // find the best plane to use as a splitter
auto bestplane = SelectSplitPlane(brushes, node, forced_quick_tree, stats); auto bestplane = SelectSplitPlane(brushes, node, forced_quick_tree, stats);
@ -1021,10 +1021,10 @@ static void BuildTree_r(tree_t *tree, node_t *node, std::vector<std::unique_ptr<
} }
// to save time/memory we can destroy node's volume at this point // to save time/memory we can destroy node's volume at this point
auto children_volumes = SplitBrush(std::move(node->volume), bestplane.value(), stats); auto children_volumes = SplitBrush(std::move(node->volume), bestplane.value(), stats);
node->volume = nullptr; node->volume = nullptr;
node->children[0]->volume = std::move(children_volumes[0]); node->children[0]->volume = std::move(children_volumes[0]);
node->children[1]->volume = std::move(children_volumes[1]); node->children[1]->volume = std::move(children_volumes[1]);
// recursively process children // recursively process children
tbb::task_group g; tbb::task_group g;
@ -1038,43 +1038,10 @@ static void BuildTree_r(tree_t *tree, node_t *node, std::vector<std::unique_ptr<
BrushBSP BrushBSP
================== ==================
*/ */
static std::unique_ptr<tree_t> BrushBSP_internal(mapentity_t *entity, std::vector<std::unique_ptr<bspbrush_t>> brushlist, std::optional<bool> forced_quick_tree) std::unique_ptr<tree_t> BrushBSP(mapentity_t *entity, const bspbrush_t::container &brushlist, std::optional<bool> forced_quick_tree)
{ {
auto tree = std::make_unique<tree_t>(); auto tree = std::make_unique<tree_t>();
size_t c_faces = 0;
size_t c_nonvisfaces = 0;
size_t c_brushes = 0;
for (const auto &b : brushlist) {
c_brushes++;
#if 0
// fixme-brushbsp: why does this just print and do nothing? should
// the brush be removed?
double volume = BrushVolume(*b);
if (volume < qbsp_options.microvolume.value()) {
logging::print("WARNING: {}: microbrush\n",
b->original->mapbrush->line);
}
#endif
for (side_t &side : b->sides) {
if (side.bevel)
continue;
if (!side.w)
continue;
if (side.onnode)
continue;
if (side.source->visible)
c_faces++;
else
c_nonvisfaces++;
}
tree->bounds += b->bounds;
}
if (brushlist.empty()) { if (brushlist.empty()) {
/* /*
* We allow an entity to be constructed with no visible brushes * We allow an entity to be constructed with no visible brushes
@ -1102,20 +1069,52 @@ static std::unique_ptr<tree_t> BrushBSP_internal(mapentity_t *entity, std::vecto
return tree; return tree;
} }
size_t c_faces = 0;
size_t c_nonvisfaces = 0;
size_t c_brushes = 0;
for (const auto &b : brushlist) {
c_brushes++;
#if 0
// fixme-brushbsp: why does this just print and do nothing? should
// the brush be removed?
double volume = BrushVolume(*b);
if (volume < qbsp_options.microvolume.value()) {
logging::print("WARNING: {}: microbrush\n",
b->mapbrush->line);
}
#endif
for (side_t &side : b->sides) {
if (side.bevel)
continue;
if (!side.w)
continue;
if (side.onnode)
continue;
if (side.source->visible)
c_faces++;
else
c_nonvisfaces++;
}
tree->bounds += b->bounds;
}
logging::print(logging::flag::STAT, " {:8} brushes\n", c_brushes); 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} visible faces\n", c_faces);
logging::print(logging::flag::STAT, " {:8} nonvisible faces\n", c_nonvisfaces); logging::print(logging::flag::STAT, " {:8} nonvisible faces\n", c_nonvisfaces);
auto node = tree->create_node(); auto node = tree->create_node();
node->volume = BrushFromBounds(tree->bounds.grow(SIDESPACE));
node->volume = BrushFromBounds(tree->bounds.grow(SIDESPACE));
node->bounds = tree->bounds.grow(SIDESPACE); node->bounds = tree->bounds.grow(SIDESPACE);
tree->headnode = node; tree->headnode = node;
bspstats_t stats{}; bspstats_t stats{};
stats.leafstats = qbsp_options.target_game->create_content_stats(); stats.leafstats = qbsp_options.target_game->create_content_stats();
BuildTree_r(tree.get(), tree->headnode, std::move(brushlist), forced_quick_tree, stats); BuildTree_r(tree.get(), tree->headnode, brushlist, forced_quick_tree, stats);
logging::print(logging::flag::STAT, " {:8} visible nodes\n", stats.c_nodes - stats.c_nonvis); logging::print(logging::flag::STAT, " {:8} visible nodes\n", stats.c_nodes - stats.c_nonvis);
if (stats.c_nonvis) { if (stats.c_nonvis) {
@ -1149,9 +1148,3 @@ static std::unique_ptr<tree_t> BrushBSP_internal(mapentity_t *entity, std::vecto
return tree; return tree;
} }
std::unique_ptr<tree_t> BrushBSP(mapentity_t *entity, const std::vector<std::unique_ptr<bspbrush_t>> &brushlist, std::optional<bool> forced_quick_tree)
{
logging::funcheader();
return BrushBSP_internal(entity, MakeBspBrushList(brushlist), forced_quick_tree);
}

View File

@ -120,16 +120,3 @@ std::tuple<std::unique_ptr<face_t>, std::unique_ptr<face_t>> SplitFace(
return {std::move(new_front), std::move(new_back)}; return {std::move(new_front), std::move(new_back)};
} }
std::vector<std::unique_ptr<bspbrush_t>> MakeBspBrushList(const bspbrush_vector_t &brushes)
{
// set the original pointers
std::vector<std::unique_ptr<bspbrush_t>> brushcopies;
for (const auto &original : brushes) {
auto copy = original->copy_unique();
copy->original = original.get();
brushcopies.push_back(std::move(copy));
}
return brushcopies;
}

View File

@ -472,7 +472,7 @@ see also FindPortalSide which populates p->side
*/ */
static std::unique_ptr<face_t> FaceFromPortal(portal_t *p, bool pside) static std::unique_ptr<face_t> FaceFromPortal(portal_t *p, bool pside)
{ {
side_t *side = p->sides[pside]; mapface_t *side = p->sides[pside];
if (!side) if (!side)
return nullptr; // portal does not bridge different visible contents return nullptr; // portal does not bridge different visible contents
@ -481,7 +481,7 @@ static std::unique_ptr<face_t> FaceFromPortal(portal_t *p, bool pside)
f->texinfo = side->texinfo; f->texinfo = side->texinfo;
f->planenum = (side->planenum & ~1) | (pside ? 1 : 0); f->planenum = (side->planenum & ~1) | (pside ? 1 : 0);
f->portal = p; f->portal = p;
f->original_side = side->source; f->original_side = side;
#if 0 #if 0
bool make_face = bool make_face =

View File

@ -1554,6 +1554,11 @@ const qbsp_plane_t &mapface_t::get_plane() const
return map.get_plane(planenum); return map.get_plane(planenum);
} }
const qbsp_plane_t &mapface_t::get_positive_plane() const
{
return map.get_plane(planenum & ~1);
}
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)
{ {
// TODO: This doesn't match how light does it (TexSpaceToWorld) // TODO: This doesn't match how light does it (TexSpaceToWorld)
@ -2798,7 +2803,7 @@ WriteBspBrushMap
from q3map from q3map
================== ==================
*/ */
void WriteBspBrushMap(const fs::path &name, const std::vector<std::unique_ptr<bspbrush_t>> &list) void WriteBspBrushMap(const fs::path &name, const bspbrush_t::container &list)
{ {
std::shared_lock lock(map_planes_lock); std::shared_lock lock(map_planes_lock);
@ -2845,7 +2850,7 @@ from q3map
*/ */
static void TestExpandBrushes(mapentity_t *src) static void TestExpandBrushes(mapentity_t *src)
{ {
std::vector<std::unique_ptr<bspbrush_t>> hull1brushes; bspbrush_t::container hull1brushes;
for (auto &mapbrush : src->mapbrushes) { for (auto &mapbrush : src->mapbrushes) {
auto hull1brush = LoadBrush(src, &mapbrush, {CONTENTS_SOLID}, auto hull1brush = LoadBrush(src, &mapbrush, {CONTENTS_SOLID},

View File

@ -349,7 +349,7 @@ std::vector<node_t *> FindOccupiedClusters(node_t *headnode)
//============================================================================= //=============================================================================
static void MarkBrushSidesInvisible(mapentity_t *entity, bspbrush_vector_t &brushes) static void MarkBrushSidesInvisible(mapentity_t *entity, bspbrush_t::container &brushes)
{ {
for (auto &brush : brushes) { for (auto &brush : brushes) {
for (auto &face : brush->sides) { for (auto &face : brush->sides) {
@ -412,13 +412,13 @@ static void MarkVisibleBrushSides_R(node_t *node)
// optimized case: just mark the brush sides in the neighbouring // optimized case: just mark the brush sides in the neighbouring
// leaf that are coplanar // leaf that are coplanar
for (auto *brush : neighbour_leaf->original_brushes) { for (auto *brush : neighbour_leaf->original_brushes) {
for (auto &side : brush->sides) { for (auto &side : brush->faces) {
// fixme-brushbsp: should this be get_plane() ? // fixme-brushbsp: should this be get_plane() ?
// fixme-brushbsp: planenum // fixme-brushbsp: planenum
if (qv::epsilonEqual(side.get_positive_plane(), portal->plane)) { if (qv::epsilonEqual(side.get_positive_plane(), portal->plane)) {
// we've found a brush side in an original brush in the neighbouring // we've found a brush side in an original brush in the neighbouring
// leaf, on a portal to this (non-opaque) leaf, so mark it as visible. // leaf, on a portal to this (non-opaque) leaf, so mark it as visible.
side.source->visible = true; side.visible = true;
} }
} }
} }
@ -600,7 +600,7 @@ get incorrectly marked as "invisible").
Special cases: structural fully covered by detail still needs to be marked "visible". Special cases: structural fully covered by detail still needs to be marked "visible".
=========== ===========
*/ */
bool FillOutside(mapentity_t *entity, tree_t *tree, const int hullnum, bspbrush_vector_t &brushes) bool FillOutside(mapentity_t *entity, tree_t *tree, const int hullnum, bspbrush_t::container &brushes)
{ {
node_t *node = tree->headnode; node_t *node = tree->headnode;
@ -720,7 +720,7 @@ bool FillOutside(mapentity_t *entity, tree_t *tree, const int hullnum, bspbrush_
return true; return true;
} }
void FillBrushEntity(mapentity_t *entity, tree_t *tree, const int hullnum, bspbrush_vector_t &brushes) void FillBrushEntity(mapentity_t *entity, tree_t *tree, const int hullnum, bspbrush_t::container &brushes)
{ {
logging::funcheader(); logging::funcheader();

View File

@ -573,8 +573,8 @@ static mapentity_t *AreanodeEntityForLeaf(node_t *node)
} }
for (auto &brush : node->original_brushes) { for (auto &brush : node->original_brushes) {
if (brush->mapbrush->func_areaportal) { if (brush->func_areaportal) {
return brush->mapbrush->func_areaportal; return brush->func_areaportal;
} }
} }
return nullptr; return nullptr;
@ -791,8 +791,8 @@ static void FindPortalSide(portal_t *p)
return; return;
// bestside[0] is the brushside visible on portal side[0] which is the positive side of the plane, always // bestside[0] is the brushside visible on portal side[0] which is the positive side of the plane, always
side_t *bestside[2] = {nullptr, nullptr}; mapface_t *bestside[2] = {nullptr, nullptr};
side_t *exactside[2] = {nullptr, nullptr}; mapface_t *exactside[2] = {nullptr, nullptr};
float bestdot = 0; float bestdot = 0;
const qbsp_plane_t &p1 = p->onnode->get_plane(); const qbsp_plane_t &p1 = p->onnode->get_plane();
@ -813,7 +813,7 @@ static void FindPortalSide(portal_t *p)
continue; continue;
} }
for (auto &side : brush->sides) { for (auto &side : brush->faces) {
if (side.bevel) if (side.bevel)
continue; continue;
if ((side.planenum & ~1) == p->onnode->planenum) { if ((side.planenum & ~1) == p->onnode->planenum) {
@ -899,7 +899,7 @@ static void MarkVisibleSides_r(node_t *node)
FindPortalSide(p); FindPortalSide(p);
for (int i = 0; i < 2; ++i) { for (int i = 0; i < 2; ++i) {
if (p->sides[i]) { if (p->sides[i]) {
p->sides[i]->source->visible = true; p->sides[i]->visible = true;
} }
} }
} }
@ -911,7 +911,7 @@ MarkVisibleSides
============= =============
*/ */
void MarkVisibleSides(tree_t *tree, mapentity_t *entity, bspbrush_vector_t &brushes) void MarkVisibleSides(tree_t *tree, mapentity_t *entity, bspbrush_t::container &brushes)
{ {
logging::funcheader(); logging::funcheader();

View File

@ -306,14 +306,13 @@ static void ExportBrushList_r(const mapentity_t *entity, node_t *node)
node->firstleafbrush = map.bsp.dleafbrushes.size(); node->firstleafbrush = map.bsp.dleafbrushes.size();
for (auto &b : node->original_brushes) { for (auto &b : node->original_brushes) {
if (!b->mapbrush->outputnumber.has_value()) { if (!b->outputnumber.has_value()) {
// FIXME b->outputnumber = {static_cast<uint32_t>(map.bsp.dbrushes.size())};
b->mapbrush->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});
for (auto &side : b->mapbrush->faces) { for (auto &side : b->faces) {
map.bsp.dbrushsides.push_back( map.bsp.dbrushsides.push_back(
{(uint32_t) ExportMapPlane(side.planenum), (int32_t)ExportMapTexinfo(side.texinfo)}); {(uint32_t) ExportMapPlane(side.planenum), (int32_t)ExportMapTexinfo(side.texinfo)});
brush.numsides++; brush.numsides++;
@ -323,7 +322,7 @@ static void ExportBrushList_r(const mapentity_t *entity, node_t *node)
brush_state.total_brushes++; brush_state.total_brushes++;
} }
map.bsp.dleafbrushes.push_back(b->mapbrush->outputnumber.value()); map.bsp.dleafbrushes.push_back(b->outputnumber.value());
} }
} }
} }
@ -442,7 +441,7 @@ static void ProcessEntity(mapentity_t *entity, const int hullnum)
// reserve enough brushes; we would only make less, // reserve enough brushes; we would only make less,
// never more // never more
bspbrush_vector_t brushes; bspbrush_t::container brushes;
brushes.reserve(entity->mapbrushes.size()); brushes.reserve(entity->mapbrushes.size());
/* /*

View File

@ -82,9 +82,8 @@ static void ConvertNodeToLeaf(node_t *node, const contentflags_t &contents)
node->original_brushes = std::move(node->children[base]->original_brushes); node->original_brushes = std::move(node->children[base]->original_brushes);
node->original_brushes.insert(node->original_brushes.end(), node->children[base ^ 1]->original_brushes.begin(), node->children[base ^ 1]->original_brushes.end()); node->original_brushes.insert(node->original_brushes.end(), node->children[base ^ 1]->original_brushes.begin(), node->children[base ^ 1]->original_brushes.end());
std::sort(node->original_brushes.begin(), node->original_brushes.end(), [](const bspbrush_t *a, const bspbrush_t *b) { // sort by pointer (since mapbrush_t is in a flat vector)
return a->mapbrush < b->mapbrush; std::sort(node->original_brushes.begin(), node->original_brushes.end());
});
auto unique = std::unique(node->original_brushes.begin(), node->original_brushes.end()); auto unique = std::unique(node->original_brushes.begin(), node->original_brushes.end());
node->original_brushes.erase(unique, node->original_brushes.end()); node->original_brushes.erase(unique, node->original_brushes.end());