Merge branch 'brushbsp' of https://github.com/ericwa/ericw-tools into brushbsp

This commit is contained in:
Jonathan 2022-07-03 14:21:39 -04:00
commit 3527dcf39b
22 changed files with 867 additions and 710 deletions

View File

@ -514,6 +514,17 @@ std::vector<qvec3f> GLM_FacePoints(const mbsp_t *bsp, const mface_t *face)
return points;
}
polylib::winding_t Face_Winding(const mbsp_t *bsp, const mface_t *face)
{
polylib::winding_t w{};
for (int j = 0; j < face->numedges; j++) {
w.push_back(Face_PointAtIndex(bsp, face, j));
}
return w;
}
qvec3f Face_Centroid(const mbsp_t *bsp, const mface_t *face)
{
auto points = GLM_FacePoints(bsp, face);

View File

@ -157,9 +157,12 @@ struct surfflags_t
// native flags value; what's written to the BSP basically
int32_t native;
// an invisible surface
// an invisible surface (Q1 "skip" texture, Q2 SURF_NODRAW)
bool is_skip;
// completely ignore, allowing non-closed brushes (Q2 SURF_SKIP)
bool is_hintskip;
// hint surface
bool is_hint;
@ -205,7 +208,7 @@ struct surfflags_t
private:
constexpr auto as_tuple() const
{
return std::tie(native, is_skip, is_hint, no_dirt, no_shadow, no_bounce, no_minlight, no_expand, light_ignore,
return std::tie(native, is_skip, is_hintskip, is_hint, no_dirt, no_shadow, no_bounce, no_minlight, no_expand, light_ignore,
phong_angle, phong_angle_concave, minlight, minlight_color, light_alpha);
}

View File

@ -22,6 +22,7 @@
#include <common/bspfile.hh>
#include <common/mathlib.hh>
#include <common/qvec.hh>
#include <common/polylib.hh>
#include <string>
#include <vector>
@ -78,6 +79,7 @@ const qvec3f &Face_PointAtIndex(const mbsp_t *bsp, const mface_t *f);
const qvec3f &Vertex_GetPos(const mbsp_t *bsp, int num);
qvec3d Face_Normal(const mbsp_t *bsp, const mface_t *f);
std::vector<qvec3f> GLM_FacePoints(const mbsp_t *bsp, const mface_t *face);
polylib::winding_t Face_Winding(const mbsp_t *bsp, const mface_t *face);
qvec3f Face_Centroid(const mbsp_t *bsp, const mface_t *face);
void Face_DebugPrint(const mbsp_t *bsp, const mface_t *face);

View File

@ -27,6 +27,7 @@
#include <optional>
class mapentity_t;
struct maptexinfo_t;
struct side_t
{
@ -42,16 +43,25 @@ struct side_t
// non-visible means we can discard the brush side
// (avoiding generating a BSP spit, so expanding it outwards)
bool bevel; // don't ever use for bsp splitting
bool tested;
const maptexinfo_t& get_texinfo() const;
};
class mapbrush_t;
struct bspbrush_t {
/**
* The brushes in the mapentity_t::brushes vector are considered originals. Brush fragments created during
* the BrushBSP will have this pointing back to the original brush in mapentity_t::brushes.
*
* fixme-brushbsp: this is supposed to be a mapbrush_t
*/
bspbrush_t *original;
uint32_t file_order;
aabb3d bounds;
int side, testside; // side of node during construction
std::vector<side_t> sides;
contentflags_t contents; /* BSP contents */
short lmshift; /* lightmap scaling (qu/lightmap pixel), passed to the light util */
@ -59,9 +69,9 @@ struct bspbrush_t {
mapentity_t *func_areaportal;
void update_bounds();
};
class mapbrush_t;
std::unique_ptr<bspbrush_t> copy_unique() const;
};
qplane3d Face_Plane(const face_t *face);
qplane3d Face_Plane(const side_t *face);

View File

@ -29,16 +29,15 @@
#include <optional>
#include <memory>
extern std::atomic<int> splitnodes;
struct bspbrush_t;
struct node_t;
struct face_t;
class mapentity_t;
struct tree_t;
void DetailToSolid(node_t *node);
void PruneNodes(node_t *node);
bool WindingIsTiny(const winding_t &w, double size = 0.2);
twosided<std::unique_ptr<bspbrush_t>> SplitBrush(std::unique_ptr<bspbrush_t> brush, const qplane3d &split);
std::unique_ptr<bspbrush_t> BrushFromBounds(const aabb3d &bounds);
tree_t *BrushBSP(std::vector<std::unique_ptr<bspbrush_t>> brushlist);
// compatibility version
tree_t *BrushBSP(mapentity_t *entity, bool midsplit);

View File

@ -32,6 +32,7 @@
#include <utility>
#include <unordered_map>
#include <list>
#include <mutex>
struct bspbrush_t;
@ -124,6 +125,8 @@ struct maptexdata_t
#include <common/imglib.hh>
extern std::recursive_mutex map_planes_lock;
struct mapdata_t
{
/* Arrays of actual items */
@ -168,6 +171,11 @@ struct mapdata_t
const std::string &texinfoTextureName(int texinfo) const { return miptexTextureName(mtexinfos.at(texinfo).miptex); }
inline qbsp_plane_t get_plane(int pnum) {
const auto lock = std::lock_guard(map_planes_lock);
return planes.at(pnum);
}
int skip_texinfo;
mapentity_t *world_entity();

View File

@ -25,6 +25,7 @@
#include <qbsp/qbsp.hh>
#include <atomic>
#include <memory>
struct side_t;
@ -43,7 +44,7 @@ struct portal_t
struct tree_t
{
node_t *headnode;
std::unique_ptr<node_t> headnode;
node_t outside_node = {}; // portals outside the world face this
aabb3d bounds;
};

View File

@ -25,6 +25,7 @@
#include <list>
#include <vector>
#include <map>
#include <memory>
#include <unordered_map>
#include <array>
#include <optional>
@ -209,6 +210,8 @@ public:
this, "oldrottex", false, &debugging_group, "use old rotate_ brush texturing aligned at (0 0 0)"};
setting_scalar epsilon{
this, "epsilon", 0.0001, 0.0, 1.0, &debugging_group, "customize epsilon value for point-on-plane checks"};
setting_scalar microvolume{
this, "microvolume", 1.0, 0.0, 1000.0, &debugging_group, "microbrush volume"};
setting_bool contenthack{this, "contenthack", false, &debugging_group,
"hack to fix leaks through solids. causes missing faces in some cases so disabled by default"};
setting_bool leaktest{this, "leaktest", false, &map_development_group, "make compilation fail if the map leaks"};
@ -349,18 +352,23 @@ struct face_t : face_fragment_t
// there is a node_t structure for every node and leaf in the bsp tree
struct bspbrush_t;
struct side_t;
struct node_t
{
// both leafs and nodes
aabb3d bounds; // bounding volume, not just points inside
node_t *parent;
// this is also a bounding volume like `bounds`
std::unique_ptr<bspbrush_t> volume; // one for each leaf/node
// information for decision nodes
int planenum; // -1 = leaf node
int firstface; // decision node only
int numfaces; // decision node only
node_t *children[2]; // children[0] = front side, children[1] = back side of plane. only valid for decision nodes
twosided<std::unique_ptr<node_t>> children; // children[0] = front side, children[1] = back side of plane. only valid for decision nodes
std::list<face_t *> facelist; // decision nodes only, list for both sides
side_t *side; // the side that created the node
// information for leafs
contentflags_t contents; // leaf nodes (0 for decision nodes)

View File

@ -28,6 +28,18 @@
#include <qbsp/map.hh>
#include <qbsp/qbsp.hh>
const maptexinfo_t& side_t::get_texinfo() const
{
return map.mtexinfos[this->texinfo];
}
std::unique_ptr<bspbrush_t> bspbrush_t::copy_unique() const
{
bspbrush_t *copy = new bspbrush_t{*this};
return std::unique_ptr<bspbrush_t>(copy);
}
/*
* Beveled clipping hull can generate many extra faces
*/
@ -56,6 +68,7 @@ Face_Plane
*/
qplane3d Face_Plane(const face_t *face)
{
const auto lock = std::lock_guard(map_planes_lock);
const qplane3d &result = map.planes.at(face->planenum);
if (face->planeside) {
@ -67,6 +80,7 @@ qplane3d Face_Plane(const face_t *face)
qplane3d Face_Plane(const side_t *face)
{
const auto lock = std::lock_guard(map_planes_lock);
const qplane3d &result = map.planes.at(face->planenum);
if (face->planeside) {
@ -85,6 +99,7 @@ Note: this will not catch 0 area polygons
*/
static void CheckFace(side_t *face, const mapface_t &sourceface)
{
const auto lock = std::lock_guard(map_planes_lock);
const qbsp_plane_t &plane = map.planes.at(face->planenum);
if (face->w.size() < 3) {
@ -209,6 +224,8 @@ inline int plane_hash_fn(const qplane3d &p)
static void PlaneHash_Add(const qplane3d &p, int index)
{
const auto lock = std::lock_guard(map_planes_lock);
const int hash = plane_hash_fn(p);
map.planehash[hash].push_back(index);
}
@ -219,6 +236,8 @@ static void PlaneHash_Add(const qplane3d &p, int index)
*/
static int NewPlane(const qplane3d &plane, planeside_t *side)
{
const auto lock = std::lock_guard(map_planes_lock);
vec_t len = qv::length(plane.normal);
if (len < 1 - options.epsilon.value() || len > 1 + options.epsilon.value())
@ -248,6 +267,8 @@ static int NewPlane(const qplane3d &plane, planeside_t *side)
*/
int FindPlane(const qplane3d &plane, planeside_t *side)
{
const auto lock = std::lock_guard(map_planes_lock);
for (int i : map.planehash[plane_hash_fn(plane)]) {
const qbsp_plane_t &p = map.planes.at(i);
if (qv::epsilonEqual(p, plane)) {
@ -269,6 +290,8 @@ int FindPlane(const qplane3d &plane, planeside_t *side)
*/
int FindPositivePlane(int planenum)
{
const auto lock = std::lock_guard(map_planes_lock);
const auto &plane = map.planes[planenum];
// already positive, or it's PLANE_ANY_x which doesn't matter

File diff suppressed because it is too large Load Diff

View File

@ -135,8 +135,8 @@ static void ExportObj_Nodes_r(const node_t *node, std::vector<const face_t *> *d
dest->push_back(face);
}
ExportObj_Nodes_r(node->children[0], dest);
ExportObj_Nodes_r(node->children[1], dest);
ExportObj_Nodes_r(node->children[0].get(), dest);
ExportObj_Nodes_r(node->children[1].get(), dest);
}
void ExportObj_Nodes(const std::string &filesuffix, const node_t *nodes)
@ -149,8 +149,8 @@ void ExportObj_Nodes(const std::string &filesuffix, const node_t *nodes)
static void ExportObj_Marksurfaces_r(const node_t *node, std::unordered_set<const face_t *> *dest)
{
if (node->planenum != PLANENUM_LEAF) {
ExportObj_Marksurfaces_r(node->children[0], dest);
ExportObj_Marksurfaces_r(node->children[1], dest);
ExportObj_Marksurfaces_r(node->children[0].get(), dest);
ExportObj_Marksurfaces_r(node->children[1].get(), dest);
return;
}

View File

@ -185,8 +185,8 @@ static void FreeNode(node_t *node)
void FreeNodes(node_t *node)
{
if (node->planenum != PLANENUM_LEAF) {
FreeNodes(node->children[0]);
FreeNodes(node->children[1]);
FreeNodes(node->children[0].get());
FreeNodes(node->children[1].get());
}
FreeNode(node);
}
@ -369,9 +369,8 @@ static int MakeFaceEdges_r(mapentity_t *entity, node_t *node, int progress)
FindFaceEdges(entity, f);
}
logging::percent(progress, splitnodes, entity);
progress = MakeFaceEdges_r(entity, node->children[0], progress);
progress = MakeFaceEdges_r(entity, node->children[1], progress);
progress = MakeFaceEdges_r(entity, node->children[0].get(), progress);
progress = MakeFaceEdges_r(entity, node->children[1].get(), progress);
return progress;
}
@ -449,8 +448,8 @@ static void GrowNodeRegion(mapentity_t *entity, node_t *node)
node->numfaces = static_cast<int>(map.bsp.dfaces.size()) - node->firstface;
GrowNodeRegion(entity, node->children[0]);
GrowNodeRegion(entity, node->children[1]);
GrowNodeRegion(entity, node->children[0].get());
GrowNodeRegion(entity, node->children[1].get());
}
static void CountFace(mapentity_t *entity, face_t *f, size_t &facesCount, size_t &vertexesCount)
@ -479,8 +478,8 @@ static void CountData_r(mapentity_t *entity, node_t *node, size_t &facesCount, s
CountFace(entity, f, facesCount, vertexesCount);
}
CountData_r(entity, node->children[0], facesCount, vertexesCount);
CountData_r(entity, node->children[1], facesCount, vertexesCount);
CountData_r(entity, node->children[0].get(), facesCount, vertexesCount);
CountData_r(entity, node->children[1].get(), facesCount, vertexesCount);
}
/*
@ -505,7 +504,6 @@ int MakeFaceEdges(mapentity_t *entity, node_t *headnode)
firstface = static_cast<int>(map.bsp.dfaces.size());
MakeFaceEdges_r(entity, headnode, 0);
logging::percent(splitnodes, splitnodes, entity == map.world_entity());
pEdgeFaces0.clear();
pEdgeFaces1.clear();
@ -536,14 +534,15 @@ static void AddMarksurfaces_r(face_t *face, face_t *face_copy, node_t *node)
return;
}
const auto lock = std::lock_guard(map_planes_lock);
const qbsp_plane_t &splitplane = map.planes.at(node->planenum);
auto [frontFragment, backFragment] = SplitFace(face_copy, splitplane);
if (frontFragment) {
AddMarksurfaces_r(face, frontFragment, node->children[0]);
AddMarksurfaces_r(face, frontFragment, node->children[0].get());
}
if (backFragment) {
AddMarksurfaces_r(face, backFragment, node->children[1]);
AddMarksurfaces_r(face, backFragment, node->children[1].get());
}
}
@ -568,15 +567,15 @@ void MakeMarkFaces(mapentity_t* entity, node_t* node)
face_t *face_copy = CopyFace(face);
if (face->planeside == 0) {
AddMarksurfaces_r(face, face_copy, node->children[0]);
AddMarksurfaces_r(face, face_copy, node->children[0].get());
} else {
AddMarksurfaces_r(face, face_copy, node->children[1]);
AddMarksurfaces_r(face, face_copy, node->children[1].get());
}
}
// process child nodes recursively
MakeMarkFaces(entity, node->children[0]);
MakeMarkFaces(entity, node->children[1]);
MakeMarkFaces(entity, node->children[0].get());
MakeMarkFaces(entity, node->children[1].get());
}
struct makefaces_stats_t {
@ -663,8 +662,8 @@ static void MakeFaces_r(node_t *node, makefaces_stats_t& stats)
// recurse down to leafs
if (node->planenum != PLANENUM_LEAF)
{
MakeFaces_r(node->children[0], stats);
MakeFaces_r(node->children[1], stats);
MakeFaces_r(node->children[0].get(), stats);
MakeFaces_r(node->children[1].get(), stats);
// merge together all visible faces on the node
if (!options.nomerge.value())

View File

@ -42,6 +42,7 @@
#include <common/qvec.hh>
mapdata_t map;
std::recursive_mutex map_planes_lock;
const std::optional<img::texture_meta> &mapdata_t::load_image_meta(const std::string_view &name)
{
@ -2372,6 +2373,8 @@ from q3map
*/
void WriteBspBrushMap(const fs::path &name, const std::vector<std::unique_ptr<bspbrush_t>> &list)
{
const auto lock = std::lock_guard(map_planes_lock);
logging::print("writing {}\n", name);
std::ofstream f(name);

View File

@ -99,6 +99,7 @@ static face_t *TryMerge(face_t *f1, face_t *f2)
// check slope of connected lines
// if the slopes are colinear, the point can be removed
const auto lock = std::lock_guard(map_planes_lock);
const auto &plane = map.planes.at(f1->planenum);
planenormal = plane.normal;
if (f1->planeside)
@ -211,8 +212,8 @@ static void CollectNodes_R(node_t *node, std::vector<node_t *> &allnodes)
return;
}
CollectNodes_R(node->children[0], allnodes);
CollectNodes_R(node->children[1], allnodes);
CollectNodes_R(node->children[0].get(), allnodes);
CollectNodes_R(node->children[1].get(), allnodes);
}
/*

View File

@ -58,20 +58,21 @@ static node_t *PointInLeaf(node_t *node, const qvec3d &point)
return node;
}
const auto lock = std::lock_guard(map_planes_lock);
const auto &plane = map.planes.at(node->planenum);
vec_t dist = plane.distance_to(point);
if (dist > 0) {
// point is on the front of the node plane
return PointInLeaf(node->children[0], point);
return PointInLeaf(node->children[0].get(), point);
} else if (dist < 0) {
// point is on the back of the node plane
return PointInLeaf(node->children[1], point);
return PointInLeaf(node->children[1].get(), point);
} else {
// point is exactly on the node plane
node_t *front = PointInLeaf(node->children[0], point);
node_t *back = PointInLeaf(node->children[1], point);
node_t *front = PointInLeaf(node->children[0].get(), point);
node_t *back = PointInLeaf(node->children[1].get(), point);
// prefer the opaque one
if (LeafSealsMap(front)) {
@ -89,8 +90,8 @@ static void ClearOccupied_r(node_t *node)
node->occupant = nullptr;
if (node->planenum != PLANENUM_LEAF) {
ClearOccupied_r(node->children[0]);
ClearOccupied_r(node->children[1]);
ClearOccupied_r(node->children[0].get());
ClearOccupied_r(node->children[1].get());
}
}
@ -117,8 +118,8 @@ static void MarkClusterOutsideDistance_R(node_t* node, int outside_distance)
node->outside_distance = outside_distance;
if (node->planenum != PLANENUM_LEAF) {
MarkClusterOutsideDistance_R(node->children[0], outside_distance);
MarkClusterOutsideDistance_R(node->children[1], outside_distance);
MarkClusterOutsideDistance_R(node->children[0].get(), outside_distance);
MarkClusterOutsideDistance_R(node->children[1].get(), outside_distance);
}
}
@ -328,8 +329,8 @@ static void FindOccupiedClusters_R(node_t *node, std::vector<node_t *>& result)
}
if (node->planenum != PLANENUM_LEAF) {
FindOccupiedClusters_R(node->children[0], result);
FindOccupiedClusters_R(node->children[1], result);
FindOccupiedClusters_R(node->children[0].get(), result);
FindOccupiedClusters_R(node->children[1].get(), result);
}
}
@ -362,8 +363,8 @@ static void MarkAllBrushSidesVisible_R(node_t *node)
{
// descend to leafs
if (node->planenum != PLANENUM_LEAF) {
MarkAllBrushSidesVisible_R(node->children[0]);
MarkAllBrushSidesVisible_R(node->children[1]);
MarkAllBrushSidesVisible_R(node->children[0].get());
MarkAllBrushSidesVisible_R(node->children[1].get());
return;
}
@ -385,8 +386,8 @@ static void MarkVisibleBrushSides_R(node_t *node)
{
// descent to leafs
if (!(node->planenum == PLANENUM_LEAF)) {
MarkVisibleBrushSides_R(node->children[0]);
MarkVisibleBrushSides_R(node->children[1]);
MarkVisibleBrushSides_R(node->children[0].get());
MarkVisibleBrushSides_R(node->children[1].get());
return;
}
@ -429,8 +430,8 @@ static void MarkVisibleBrushSides_R(node_t *node)
static void OutLeafsToSolid_r(node_t *node, int *outleafs_count, settings::filltype_t filltype)
{
if (node->planenum != PLANENUM_LEAF) {
OutLeafsToSolid_r(node->children[0], outleafs_count, filltype);
OutLeafsToSolid_r(node->children[1], outleafs_count, filltype);
OutLeafsToSolid_r(node->children[0].get(), outleafs_count, filltype);
OutLeafsToSolid_r(node->children[1].get(), outleafs_count, filltype);
return;
}
@ -467,8 +468,8 @@ static int OutLeafsToSolid(node_t *node, settings::filltype_t filltype)
static void SetOccupied_R(node_t *node, int dist)
{
if (node->planenum != PLANENUM_LEAF) {
SetOccupied_R(node->children[0], dist);
SetOccupied_R(node->children[1], dist);
SetOccupied_R(node->children[0].get(), dist);
SetOccupied_R(node->children[1].get(), dist);
}
node->occupied = dist;
@ -596,7 +597,7 @@ Special cases: structural fully covered by detail still needs to be marked "visi
*/
bool FillOutside(mapentity_t *entity, tree_t *tree, const int hullnum)
{
node_t *node = tree->headnode;
node_t *node = tree->headnode.get();
logging::print(logging::flag::PROGRESS, "---- {} ----\n", __func__);
@ -716,9 +717,9 @@ void FillBrushEntity(mapentity_t* entity, tree_t *tree, const int hullnum)
logging::print(logging::flag::PROGRESS, "---- {} ----\n", __func__);
// Clear the outside filling state on all nodes
ClearOccupied_r(tree->headnode);
ClearOccupied_r(tree->headnode.get());
MarkBrushSidesInvisible(entity);
MarkVisibleBrushSides_R(tree->headnode);
MarkVisibleBrushSides_R(tree->headnode.get());
}

View File

@ -39,7 +39,7 @@ contentflags_t ClusterContents(const node_t *node)
return node->contents;
return options.target_game->cluster_contents(
ClusterContents(node->children[0]), ClusterContents(node->children[1]));
ClusterContents(node->children[0].get()), ClusterContents(node->children[1].get()));
}
/*
@ -193,9 +193,9 @@ void MakeHeadnodePortals(tree_t *tree)
p->winding = BaseWindingForPlane(pl);
if (side)
AddPortalToNodes(p, &tree->outside_node, tree->headnode);
AddPortalToNodes(p, &tree->outside_node, tree->headnode.get());
else
AddPortalToNodes(p, tree->headnode, &tree->outside_node);
AddPortalToNodes(p, tree->headnode.get(), &tree->outside_node);
}
// clip the basewindings by all the other planes
@ -222,6 +222,7 @@ Creates a winding from the given node plane, clipped by all parent nodes.
std::optional<winding_t> BaseWindingForNode(node_t *node)
{
const auto lock = std::lock_guard(map_planes_lock);
auto plane = map.planes.at(node->planenum);
std::optional<winding_t> w = BaseWindingForPlane(plane);
@ -231,7 +232,7 @@ std::optional<winding_t> BaseWindingForNode(node_t *node)
{
plane = map.planes.at(np->planenum);
const planeside_t keep = (np->children[0] == node) ?
const planeside_t keep = (np->children[0].get() == node) ?
SIDE_FRONT : SIDE_BACK;
w = w->clip(plane, BASE_WINDING_EPSILON, false)[keep];
@ -254,6 +255,8 @@ portals in the node.
*/
void MakeNodePortal(node_t *node, portalstats_t &stats)
{
const auto lock = std::lock_guard(map_planes_lock);
auto w = BaseWindingForNode(node);
// clip the portal by all the other portals in the node
@ -291,7 +294,7 @@ void MakeNodePortal(node_t *node, portalstats_t &stats)
new_portal->planenum = node->planenum;
new_portal->onnode = node;
new_portal->winding = w;
AddPortalToNodes(new_portal, node->children[0], node->children[1]);
AddPortalToNodes(new_portal, node->children[0].get(), node->children[1].get());
}
/*
@ -304,9 +307,11 @@ children have portals instead of node.
*/
void SplitNodePortals(node_t *node, portalstats_t &stats)
{
const auto lock = std::lock_guard(map_planes_lock);
const auto plane = map.planes.at(node->planenum);
node_t *f = node->children[0];
node_t *b = node->children[1];
node_t *f = node->children[0].get();
node_t *b = node->children[1].get();
portal_t *next_portal = nullptr;
for (portal_t *p = node->portals; p ; p = next_portal)
@ -433,8 +438,8 @@ void MakeTreePortals_r(node_t *node, portalstats_t &stats)
MakeNodePortal(node, stats);
SplitNodePortals(node, stats);
MakeTreePortals_r(node->children[0], stats);
MakeTreePortals_r(node->children[1], stats);
MakeTreePortals_r(node->children[0].get(), stats);
MakeTreePortals_r(node->children[1].get(), stats);
}
/*
@ -444,14 +449,14 @@ MakeTreePortals
*/
void MakeTreePortals(tree_t *tree)
{
FreeTreePortals_r(tree->headnode);
FreeTreePortals_r(tree->headnode.get());
AssertNoPortals(tree->headnode);
AssertNoPortals(tree->headnode.get());
portalstats_t stats{};
MakeHeadnodePortals(tree);
MakeTreePortals_r(tree->headnode, stats);
MakeTreePortals_r(tree->headnode.get(), stats);
}
void AssertNoPortals(node_t *node)
@ -459,8 +464,8 @@ void AssertNoPortals(node_t *node)
Q_assert(!node->portals);
if (node->planenum != PLANENUM_LEAF) {
AssertNoPortals(node->children[0]);
AssertNoPortals(node->children[1]);
AssertNoPortals(node->children[0].get());
AssertNoPortals(node->children[1].get());
}
}
@ -475,8 +480,8 @@ void FreeTreePortals_r(node_t *node)
portal_t *p, *nextp;
if (node->planenum != PLANENUM_LEAF) {
FreeTreePortals_r(node->children[0]);
FreeTreePortals_r(node->children[1]);
FreeTreePortals_r(node->children[0].get());
FreeTreePortals_r(node->children[1].get());
}
for (p = node->portals; p; p = nextp) {
@ -504,8 +509,8 @@ static void ApplyArea_r(node_t *node)
node->area = map.c_areas;
if (node->planenum != PLANENUM_LEAF) {
ApplyArea_r(node->children[0]);
ApplyArea_r(node->children[1]);
ApplyArea_r(node->children[0].get());
ApplyArea_r(node->children[1].get());
}
}
@ -513,10 +518,10 @@ static mapentity_t *AreanodeEntityForLeaf(node_t *node)
{
// if detail cluster, search the children recursively
if (node->planenum != PLANENUM_LEAF) {
if (auto *child0result = AreanodeEntityForLeaf(node->children[0]); child0result) {
if (auto *child0result = AreanodeEntityForLeaf(node->children[0].get()); child0result) {
return child0result;
}
return AreanodeEntityForLeaf(node->children[1]);
return AreanodeEntityForLeaf(node->children[1].get());
}
for (auto &brush : node->original_brushes) {
@ -629,8 +634,8 @@ area set, flood fill out from there
static void SetAreaPortalAreas_r(node_t *node)
{
if (node->planenum != PLANENUM_LEAF) {
SetAreaPortalAreas_r(node->children[0]);
SetAreaPortalAreas_r(node->children[1]);
SetAreaPortalAreas_r(node->children[0].get());
SetAreaPortalAreas_r(node->children[1].get());
return;
}
@ -735,6 +740,8 @@ Finds a brush side to use for texturing the given portal
*/
static void FindPortalSide(portal_t *p)
{
const auto lock = std::lock_guard(map_planes_lock);
// decide which content change is strongest
// solid > lava > water, etc
contentflags_t viscontents = options.target_game->visible_contents(p->nodes[0]->contents, p->nodes[1]->contents);
@ -799,8 +806,8 @@ static void MarkVisibleSides_r(node_t *node)
{
if (node->planenum != PLANENUM_LEAF)
{
MarkVisibleSides_r(node->children[0]);
MarkVisibleSides_r(node->children[1]);
MarkVisibleSides_r(node->children[0].get());
MarkVisibleSides_r(node->children[1].get());
return;
}
@ -840,5 +847,5 @@ void MarkVisibleSides(tree_t *tree, mapentity_t* entity)
}
// set visible flags on the sides that are used by portals
MarkVisibleSides_r (tree->headnode);
MarkVisibleSides_r(tree->headnode.get());
}

View File

@ -48,14 +48,16 @@ static void WriteFloat(std::ofstream &portalFile, vec_t v)
static void WritePortals_r(node_t *node, std::ofstream &portalFile, bool clusters)
{
const auto lock = std::lock_guard(map_planes_lock);
const portal_t *p, *next;
std::optional<winding_t> w;
int i, front, back;
qplane3d plane2;
if (node->planenum != PLANENUM_LEAF && !node->detail_separator) {
WritePortals_r(node->children[0], portalFile, clusters);
WritePortals_r(node->children[1], portalFile, clusters);
WritePortals_r(node->children[0].get(), portalFile, clusters);
WritePortals_r(node->children[1].get(), portalFile, clusters);
return;
}
if (node->contents.is_solid(options.target_game))
@ -101,8 +103,8 @@ static void WritePortals_r(node_t *node, std::ofstream &portalFile, bool cluster
static int WriteClusters_r(node_t *node, std::ofstream &portalFile, int viscluster)
{
if (node->planenum != PLANENUM_LEAF) {
viscluster = WriteClusters_r(node->children[0], portalFile, viscluster);
viscluster = WriteClusters_r(node->children[1], portalFile, viscluster);
viscluster = WriteClusters_r(node->children[0].get(), portalFile, viscluster);
viscluster = WriteClusters_r(node->children[1].get(), portalFile, viscluster);
return viscluster;
}
if (node->contents.is_solid(options.target_game))
@ -167,8 +169,8 @@ static void NumberLeafs_r(node_t *node, portal_state_t *state, int cluster)
node->viscluster = cluster;
CountPortals(node, state);
}
NumberLeafs_r(node->children[0], state, cluster);
NumberLeafs_r(node->children[1], state, cluster);
NumberLeafs_r(node->children[0].get(), state, cluster);
NumberLeafs_r(node->children[1].get(), state, cluster);
return;
}
@ -264,8 +266,8 @@ void CreateVisPortals_r(node_t *node, portalstats_t &stats)
MakeNodePortal(node, stats);
SplitNodePortals(node, stats);
CreateVisPortals_r(node->children[0], stats);
CreateVisPortals_r(node->children[1], stats);
CreateVisPortals_r(node->children[0].get(), stats);
CreateVisPortals_r(node->children[1].get(), stats);
}
/*
@ -279,16 +281,16 @@ void WritePortalFile(tree_t *tree)
portal_state_t state{};
FreeTreePortals_r(tree->headnode);
FreeTreePortals_r(tree->headnode.get());
AssertNoPortals(tree->headnode);
AssertNoPortals(tree->headnode.get());
MakeHeadnodePortals(tree);
portalstats_t stats{};
CreateVisPortals_r(tree->headnode, stats);
CreateVisPortals_r(tree->headnode.get(), stats);
/* save portal file for vis tracing */
WritePortalfile(tree->headnode, &state);
WritePortalfile(tree->headnode.get(), &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);

View File

@ -40,6 +40,7 @@
#include <qbsp/qbsp.hh>
#include <qbsp/writebsp.hh>
#include <qbsp/outside.hh>
#include <qbsp/tree.hh>
#include <fmt/chrono.h>
@ -283,8 +284,8 @@ static void ExportBrushList_r(const mapentity_t *entity, node_t *node)
return;
}
ExportBrushList_r(entity, node->children[0]);
ExportBrushList_r(entity, node->children[1]);
ExportBrushList_r(entity, node->children[0].get());
ExportBrushList_r(entity, node->children[1].get());
}
/*
@ -297,6 +298,8 @@ against axial bounding boxes
*/
static std::vector<std::tuple<size_t, const side_t *>> AddBrushBevels(const bspbrush_t &b)
{
const auto lock = std::lock_guard(map_planes_lock);
// add already-present planes
std::vector<std::tuple<size_t, const side_t *>> planes;
@ -579,11 +582,11 @@ static void ProcessEntity(mapentity_t *entity, const int hullnum)
// fill again so PruneNodes works
MakeTreePortals(tree);
FillOutside(entity, tree, hullnum);
PruneNodes(tree->headnode);
DetailToSolid(tree->headnode);
PruneNodes(tree->headnode.get());
DetailToSolid(tree->headnode.get());
}
}
ExportClipNodes(entity, tree->headnode, hullnum);
ExportClipNodes(entity, tree->headnode.get(), hullnum);
// fixme-brushbsp: return here?
} else {
@ -628,8 +631,8 @@ static void ProcessEntity(mapentity_t *entity, const int hullnum)
// Area portals
if (options.target_game->id == GAME_QUAKE_II) {
FloodAreas(entity, tree->headnode);
EmitAreaPortals(tree->headnode);
FloodAreas(entity, tree->headnode.get());
EmitAreaPortals(tree->headnode.get());
}
} else {
FillBrushEntity(entity, tree, hullnum);
@ -641,39 +644,39 @@ static void ProcessEntity(mapentity_t *entity, const int hullnum)
MakeTreePortals(tree);
MarkVisibleSides(tree, entity);
MakeFaces(tree->headnode);
MakeFaces(tree->headnode.get());
FreeTreePortals_r(tree->headnode);
PruneNodes(tree->headnode);
FreeTreePortals_r(tree->headnode.get());
PruneNodes(tree->headnode.get());
if (hullnum <= 0 && entity == map.world_entity() && (!map.leakfile || options.keepprt.value())) {
WritePortalFile(tree);
}
// needs to come after any face creation
MakeMarkFaces(entity, tree->headnode);
MakeMarkFaces(entity, tree->headnode.get());
// convert detail leafs to solid (in case we didn't make the call above)
DetailToSolid(tree->headnode);
DetailToSolid(tree->headnode.get());
// fixme-brushbsp: prune nodes
if (!options.notjunc.value()) {
TJunc(entity, tree->headnode);
TJunc(entity, tree->headnode.get());
}
if (options.objexport.value() && entity == map.world_entity()) {
ExportObj_Nodes("pre_makefaceedges_plane_faces", tree->headnode);
ExportObj_Marksurfaces("pre_makefaceedges_marksurfaces", tree->headnode);
ExportObj_Nodes("pre_makefaceedges_plane_faces", tree->headnode.get());
ExportObj_Marksurfaces("pre_makefaceedges_marksurfaces", tree->headnode.get());
}
firstface = MakeFaceEdges(entity, tree->headnode);
firstface = MakeFaceEdges(entity, tree->headnode.get());
if (options.target_game->id == GAME_QUAKE_II) {
ExportBrushList(entity, tree->headnode);
ExportBrushList(entity, tree->headnode.get());
}
ExportDrawNodes(entity, tree->headnode, firstface);
ExportDrawNodes(entity, tree->headnode.get(), firstface);
}
FreeBrushes(entity);
@ -763,6 +766,8 @@ hull sizes
static void BSPX_Brushes_AddModel(
struct bspxbrushes_s *ctx, int modelnum, std::vector<std::unique_ptr<bspbrush_t>> &brushes)
{
const auto lock = std::lock_guard(map_planes_lock);
bspxbrushes_permodel permodel{1, modelnum};
for (auto &b : brushes) {

View File

@ -1,6 +1,7 @@
#include <catch2/catch_all.hpp>
#include <qbsp/brush.hh>
#include <qbsp/brushbsp.hh>
#include <qbsp/qbsp.hh>
#include <qbsp/map.hh>
#include <common/fs.hh>
@ -870,7 +871,19 @@ TEST_CASE("merge", "[testmaps_q1]")
const auto [bsp, bspx, prt] = LoadTestmapQ1("qbsp_merge.map");
REQUIRE_FALSE(prt.has_value());
REQUIRE(6 == bsp.dfaces.size());
REQUIRE(bsp.dfaces.size() >= 6);
// BrushBSP does a split through the middle first to keep the BSP balanced, which prevents
// two of the side face from being merged
REQUIRE(bsp.dfaces.size() <= 8);
const auto exp_bounds = aabb3d{{48,0,96}, {224,96,96}};
auto* top_face = BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], {48,0,96}, {0,0,1});
const auto top_winding = Face_Winding(&bsp, top_face);
CHECK(top_winding.bounds().mins() == exp_bounds.mins());
CHECK(top_winding.bounds().maxs() == exp_bounds.maxs());
}
TEST_CASE("tjunc_many_sided_face", "[testmaps_q1]")
@ -1677,3 +1690,37 @@ TEST_CASE("winding", "[benchmark][.releaseonly]") {
ankerl::nanobench::doNotOptimizeAway(temp);
});
}
TEST_CASE("BrushFromBounds") {
map.reset();
options.reset();
options.worldextent.setValue(1024);
auto brush = BrushFromBounds({{2,2,2}, {32, 32, 32}});
CHECK(brush->sides.size() == 6);
const auto top_winding = winding_t{{2, 2, 32}, {2, 32, 32}, {32, 32, 32}, {32, 2, 32}};
const auto bottom_winding = winding_t{{32, 2, 2},{32, 32, 2}, {2, 32, 2}, {2, 2, 2}};
int found = 0;
for (auto &side : brush->sides) {
CHECK(side.w);
if (side.w.directional_equal(top_winding)) {
found++;
auto plane = Face_Plane(&side);
CHECK(plane.normal == qvec3d{0,0,1});
CHECK(plane.dist == 32);
}
if (side.w.directional_equal(bottom_winding)) {
found++;
auto plane = Face_Plane(&side);
CHECK(plane.normal == qvec3d{0,0,-1});
CHECK(plane.dist == -2);
}
}
CHECK(found == 2);
}

View File

@ -370,8 +370,8 @@ static void tjunc_count_r(node_t *node)
cWVerts += f->w.size();
}
tjunc_count_r(node->children[0]);
tjunc_count_r(node->children[1]);
tjunc_count_r(node->children[0].get());
tjunc_count_r(node->children[1].get());
}
static void tjunc_find_r(node_t *node)
@ -383,8 +383,8 @@ static void tjunc_find_r(node_t *node)
AddFaceEdges(f);
}
tjunc_find_r(node->children[0]);
tjunc_find_r(node->children[1]);
tjunc_find_r(node->children[0].get());
tjunc_find_r(node->children[1].get());
}
static void tjunc_fix_r(node_t *node)
@ -396,8 +396,8 @@ static void tjunc_fix_r(node_t *node)
FixFaceEdges(face);
}
tjunc_fix_r(node->children[0]);
tjunc_fix_r(node->children[1]);
tjunc_fix_r(node->children[0].get());
tjunc_fix_r(node->children[1].get());
}
/*

View File

@ -23,6 +23,7 @@
#include <common/vectorutils.hh>
#include <qbsp/qbsp.hh>
#include <qbsp/brush.hh>
//============================================================================
@ -37,7 +38,6 @@ static void ConvertNodeToLeaf(node_t *node, const contentflags_t &contents)
node->planenum = PLANENUM_LEAF;
for (int i = 0; i < 2; ++i) {
delete node->children[i];
node->children[i] = nullptr;
}
for (auto *face : node->facelist) {
@ -68,8 +68,8 @@ void DetailToSolid(node_t *node)
*/
return;
} else {
DetailToSolid(node->children[0]);
DetailToSolid(node->children[1]);
DetailToSolid(node->children[0].get());
DetailToSolid(node->children[1].get());
// If both children are solid, we can merge the two leafs into one.
// DarkPlaces has an assertion that fails if both children are
@ -89,8 +89,8 @@ static void PruneNodes_R(node_t *node, int &count_pruned)
return;
}
PruneNodes_R(node->children[0], count_pruned);
PruneNodes_R(node->children[1], count_pruned);
PruneNodes_R(node->children[0].get(), count_pruned);
PruneNodes_R(node->children[1].get(), count_pruned);
if (node->children[0]->planenum == PLANENUM_LEAF && node->children[0]->contents.is_solid(options.target_game) &&
node->children[1]->planenum == PLANENUM_LEAF && node->children[1]->contents.is_solid(options.target_game)) {

View File

@ -37,6 +37,7 @@ using nlohmann::json;
*/
size_t ExportMapPlane(size_t planenum)
{
const auto lock = std::lock_guard(map_planes_lock);
qbsp_plane_t &plane = map.planes.at(planenum);
if (plane.outputplanenum.has_value())
@ -106,8 +107,8 @@ static size_t ExportClipNodes(mapentity_t *entity, node_t *node)
const size_t nodenum = map.bsp.dclipnodes.size();
map.bsp.dclipnodes.emplace_back();
const int child0 = ExportClipNodes(entity, node->children[0]);
const int child1 = ExportClipNodes(entity, node->children[1]);
const int child0 = ExportClipNodes(entity, node->children[0].get());
const int child1 = ExportClipNodes(entity, node->children[1].get());
// Careful not to modify the vector while using this clipnode pointer
bsp2_dclipnode_t &clipnode = map.bsp.dclipnodes[nodenum];
@ -222,12 +223,12 @@ static void ExportDrawNodes(mapentity_t *entity, node_t *node)
int32_t nextLeafIndex = static_cast<int32_t>(map.bsp.dleafs.size());
const int32_t childnum = -(nextLeafIndex + 1);
dnode->children[i] = childnum;
ExportLeaf(entity, node->children[i]);
ExportLeaf(entity, node->children[i].get());
}
} else {
const int32_t childnum = static_cast<int32_t>(map.bsp.dnodes.size());
dnode->children[i] = childnum;
ExportDrawNodes(entity, node->children[i]);
ExportDrawNodes(entity, node->children[i].get());
// Important: our dnode pointer may be invalid after the recursive call, if the vector got resized.
// So re-set the pointer.