diff --git a/include/common/mathlib.hh b/include/common/mathlib.hh index 2337d59f..bbb3222f 100644 --- a/include/common/mathlib.hh +++ b/include/common/mathlib.hh @@ -53,7 +53,7 @@ constexpr vec_t DIST_EPSILON = 0.0001; constexpr vec_t DEGREES_EPSILON = 0.001; constexpr vec_t DEFAULT_ON_EPSILON = 0.1; -enum side_t : int8_t +enum planeside_t : int8_t { SIDE_FRONT, SIDE_BACK, diff --git a/include/common/polylib.hh b/include/common/polylib.hh index 7d431b75..e7fea2fc 100644 --- a/include/common/polylib.hh +++ b/include/common/polylib.hh @@ -540,7 +540,7 @@ public: // dists/sides can be null, or must have (size() + 1) reserved inline std::array calc_sides( - const qplane3d &plane, vec_t *dists, side_t *sides, const vec_t &on_epsilon = DEFAULT_ON_EPSILON) const + const qplane3d &plane, vec_t *dists, planeside_t *sides, const vec_t &on_epsilon = DEFAULT_ON_EPSILON) const { std::array counts{}; @@ -554,7 +554,7 @@ public: dists[i] = dot; } - side_t side; + planeside_t side; if (dot > on_epsilon) side = SIDE_FRONT; @@ -593,7 +593,7 @@ public: const qplane3d &plane, const vec_t &on_epsilon = DEFAULT_ON_EPSILON, const bool &keepon = false) const { vec_t *dists = (vec_t *)alloca(sizeof(vec_t) * (count + 1)); - side_t *sides = (side_t *)alloca(sizeof(side_t) * (count + 1)); + planeside_t *sides = (planeside_t *)alloca(sizeof(planeside_t) * (count + 1)); std::array counts = calc_sides(plane, dists, sides, on_epsilon); diff --git a/include/qbsp/brush.hh b/include/qbsp/brush.hh index 1e7f60c0..1cac2765 100644 --- a/include/qbsp/brush.hh +++ b/include/qbsp/brush.hh @@ -28,16 +28,33 @@ class mapentity_t; -struct brush_t +struct side_t { + winding_t w; + int planenum; + planeside_t planeside; // which side is the front of the face + int texinfo; + + int16_t lmshift; + + qvec3d origin; + vec_t radius; + + bool onnode; // has this face been used as a BSP node plane yet? + bool visible = true; // can any part of this side be seen from non-void parts of the level? + // non-visible means we can discard the brush side + // (avoiding generating a BSP spit, so expanding it outwards) +}; + +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. */ - brush_t *original; + bspbrush_t *original; uint32_t file_order; aabb3d bounds; - std::vector faces; + std::vector sides; contentflags_t contents; /* BSP contents */ short lmshift; /* lightmap scaling (qu/lightmap pixel), passed to the light util */ std::optional outputnumber; /* only set for original brushes */ @@ -49,6 +66,7 @@ struct brush_t class mapbrush_t; qplane3d Face_Plane(const face_t *face); +qplane3d Face_Plane(const side_t *face); enum class rotation_t { @@ -57,10 +75,10 @@ enum class rotation_t origin_brush }; -std::optional LoadBrush(const mapentity_t *src, const mapbrush_t *mapbrush, const contentflags_t &contents, +std::optional LoadBrush(const mapentity_t *src, const mapbrush_t *mapbrush, const contentflags_t &contents, const qvec3d &rotate_offset, const rotation_t rottype, const int hullnum); void FreeBrushes(mapentity_t *ent); -int FindPlane(const qplane3d &plane, side_t *side); +int FindPlane(const qplane3d &plane, planeside_t *side); int FindPositivePlane(int planenum); -int FindPositivePlane(const qplane3d &plane, side_t *side); +int FindPositivePlane(const qplane3d &plane, planeside_t *side); diff --git a/include/qbsp/solidbsp.hh b/include/qbsp/brushbsp.hh similarity index 90% rename from include/qbsp/solidbsp.hh rename to include/qbsp/brushbsp.hh index bbf4a8e8..c00689fa 100644 --- a/include/qbsp/solidbsp.hh +++ b/include/qbsp/brushbsp.hh @@ -31,7 +31,7 @@ extern std::atomic splitnodes; -struct brush_t; +struct bspbrush_t; struct node_t; struct face_t; class mapentity_t; @@ -40,5 +40,5 @@ struct tree_t; void DetailToSolid(node_t *node); void PruneNodes(node_t *node); bool WindingIsTiny(const winding_t &w, double size = 0.2); -twosided> SplitBrush(std::unique_ptr brush, const qplane3d &split); +twosided> SplitBrush(std::unique_ptr brush, const qplane3d &split); tree_t *BrushBSP(mapentity_t *entity, bool midsplit); diff --git a/include/qbsp/csg4.hh b/include/qbsp/csg4.hh index 80022ad3..d0a518a9 100644 --- a/include/qbsp/csg4.hh +++ b/include/qbsp/csg4.hh @@ -27,13 +27,15 @@ #include #include -struct brush_t; +struct bspbrush_t; struct face_t; +struct side_t; face_t *NewFaceFromFace(const face_t *in); face_t *CopyFace(const face_t *in); face_t *MirrorFace(const face_t *face); std::tuple SplitFace(face_t *in, const qplane3d &split); void UpdateFaceSphere(face_t *in); -bool BrushGE(const brush_t &a, const brush_t &b); -std::vector> ChopBrushes(const std::vector> &input); +void UpdateFaceSphere(side_t *in); +bool BrushGE(const bspbrush_t &a, const bspbrush_t &b); +std::vector> ChopBrushes(const std::vector> &input); diff --git a/include/qbsp/map.hh b/include/qbsp/map.hh index 423c591b..dae77604 100644 --- a/include/qbsp/map.hh +++ b/include/qbsp/map.hh @@ -33,7 +33,7 @@ #include #include -struct brush_t; +struct bspbrush_t; struct qbsp_plane_t : qplane3d { @@ -102,7 +102,7 @@ public: entdict_t epairs; aabb3d bounds; - std::vector> brushes; + std::vector> brushes; int firstoutputfacenumber = -1; std::optional outputmodelnumber = std::nullopt; @@ -218,7 +218,7 @@ constexpr int HULL_COLLISION = -1; /* Create BSP brushes from map brushes */ void Brush_LoadEntity(mapentity_t *entity, const int hullnum); -std::list CSGFace(face_t *srcface, const mapentity_t* srcentity, const brush_t *srcbrush, const node_t *srcnode); +std::list CSGFace(face_t *srcface, const mapentity_t* srcentity, const bspbrush_t *srcbrush, const node_t *srcnode); void TJunc(const mapentity_t *entity, node_t *headnode); int MakeFaceEdges(mapentity_t *entity, node_t *headnode); void ExportClipNodes(mapentity_t *entity, node_t *headnode, const int hullnum); @@ -232,10 +232,10 @@ void BSPX_Brushes_Finalize(struct bspxbrushes_s *ctx); void BSPX_Brushes_Init(struct bspxbrushes_s *ctx); void ExportObj_Faces(const std::string &filesuffix, const std::vector &faces); -void ExportObj_Brushes(const std::string &filesuffix, const std::vector &brushes); +void ExportObj_Brushes(const std::string &filesuffix, const std::vector &brushes); 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> &list); +void WriteBspBrushMap(const fs::path &name, const std::vector> &list); bool IsValidTextureProjection(const qvec3f &faceNormal, const qvec3f &s_vec, const qvec3f &t_vec); diff --git a/include/qbsp/portals.hh b/include/qbsp/portals.hh index a48c5a1a..76544319 100644 --- a/include/qbsp/portals.hh +++ b/include/qbsp/portals.hh @@ -26,6 +26,8 @@ #include +struct side_t; + struct portal_t { int planenum; @@ -35,7 +37,7 @@ struct portal_t std::optional winding; bool sidefound; // false if ->side hasn't been checked - face_t *side; // NULL = non-visible // fixme-brushbsp: change to side_t + side_t *side; // NULL = non-visible face_t *face[2]; // output face in bsp file }; diff --git a/include/qbsp/qbsp.hh b/include/qbsp/qbsp.hh index eeb12dad..2ea7c86b 100644 --- a/include/qbsp/qbsp.hh +++ b/include/qbsp/qbsp.hh @@ -332,10 +332,10 @@ struct portal_t; struct face_t : face_fragment_t { int planenum; - side_t planeside; // which side is the front of the face + planeside_t planeside; // which side is the front of the face int texinfo; - twosided contents; - twosided lmshift; + contentflags_t contents; // contents on the front of the face + int16_t lmshift; qvec3d origin; vec_t radius; @@ -343,17 +343,12 @@ struct face_t : face_fragment_t // filled by TJunc std::vector fragments; - // fixme-brushbsp: move to a brush_side_t struct - bool onnode; // has this face been used as a BSP node plane yet? - bool visible = true; // can any part of this side be seen from non-void parts of the level? - // non-visible means we can discard the brush side - // (avoiding generating a BSP spit, so expanding it outwards) portal_t *portal; }; // there is a node_t structure for every node and leaf in the bsp tree -struct brush_t; +struct bspbrush_t; struct node_t { @@ -369,7 +364,7 @@ struct node_t // information for leafs contentflags_t contents; // leaf nodes (0 for decision nodes) - std::vector original_brushes; + std::vector original_brushes; std::vector markfaces; // leaf nodes only, point to node faces portal_t *portals; int visleafnum; // -1 = solid diff --git a/qbsp/CMakeLists.txt b/qbsp/CMakeLists.txt index a5a0fb89..59e31932 100644 --- a/qbsp/CMakeLists.txt +++ b/qbsp/CMakeLists.txt @@ -9,7 +9,7 @@ set(QBSP_INCLUDES ${CMAKE_SOURCE_DIR}/include/qbsp/outside.hh ${CMAKE_SOURCE_DIR}/include/qbsp/portals.hh ${CMAKE_SOURCE_DIR}/include/qbsp/prtfile.hh - ${CMAKE_SOURCE_DIR}/include/qbsp/solidbsp.hh + ${CMAKE_SOURCE_DIR}/include/qbsp/brushbsp.hh ${CMAKE_SOURCE_DIR}/include/qbsp/surfaces.hh ${CMAKE_SOURCE_DIR}/include/qbsp/writebsp.hh) @@ -22,7 +22,7 @@ set(QBSP_SOURCES ${CMAKE_SOURCE_DIR}/qbsp/portals.cc ${CMAKE_SOURCE_DIR}/qbsp/prtfile.cc ${CMAKE_SOURCE_DIR}/qbsp/qbsp.cc - ${CMAKE_SOURCE_DIR}/qbsp/solidbsp.cc + ${CMAKE_SOURCE_DIR}/qbsp/brushbsp.cc ${CMAKE_SOURCE_DIR}/qbsp/surfaces.cc ${CMAKE_SOURCE_DIR}/qbsp/tjunc.cc ${CMAKE_SOURCE_DIR}/qbsp/wad.cc diff --git a/qbsp/brush.cc b/qbsp/brush.cc index 59f98c20..db96de81 100644 --- a/qbsp/brush.cc +++ b/qbsp/brush.cc @@ -65,6 +65,17 @@ qplane3d Face_Plane(const face_t *face) return result; } +qplane3d Face_Plane(const side_t *face) +{ + const qplane3d &result = map.planes.at(face->planenum); + + if (face->planeside) { + return -result; + } + + return result; +} + /* ================= CheckFace @@ -72,7 +83,7 @@ CheckFace Note: this will not catch 0 area polygons ================= */ -static void CheckFace(face_t *face, const mapface_t &sourceface) +static void CheckFace(side_t *face, const mapface_t &sourceface) { const qbsp_plane_t &plane = map.planes.at(face->planenum); @@ -206,7 +217,7 @@ static void PlaneHash_Add(const qplane3d &p, int index) * NewPlane * - Returns a global plane number and the side that will be the front */ -static int NewPlane(const qplane3d &plane, side_t *side) +static int NewPlane(const qplane3d &plane, planeside_t *side) { vec_t len = qv::length(plane.normal); @@ -235,7 +246,7 @@ static int NewPlane(const qplane3d &plane, side_t *side) * - Returns a global plane number and the side that will be the front * - if `side` is null, only an exact match will be fetched. */ -int FindPlane(const qplane3d &plane, side_t *side) +int FindPlane(const qplane3d &plane, planeside_t *side) { for (int i : map.planehash[plane_hash_fn(plane)]) { const qbsp_plane_t &p = map.planes.at(i); @@ -268,7 +279,7 @@ int FindPositivePlane(int planenum) return FindPlane(-plane, nullptr); } -int FindPositivePlane(const qplane3d &plane, side_t *side) +int FindPositivePlane(const qplane3d &plane, planeside_t *side) { int planenum = FindPlane(plane, side); int positive_plane = FindPositivePlane(planenum); @@ -358,13 +369,13 @@ static bool MapBrush_IsHint(const mapbrush_t &brush) CreateBrushFaces ================= */ -static std::vector CreateBrushFaces(const mapentity_t *src, hullbrush_t *hullbrush, const int hullnum, +static std::vector CreateBrushFaces(const mapentity_t *src, hullbrush_t *hullbrush, const int hullnum, const rotation_t rottype = rotation_t::none, const qvec3d &rotate_offset = {}) { vec_t r; std::optional w; qbsp_plane_t plane; - std::vector facelist; + std::vector facelist; qvec3d point; vec_t max, min; @@ -405,7 +416,7 @@ static std::vector CreateBrushFaces(const mapentity_t *src, hullbrush_t } // this face is a keeper - face_t &f = facelist.emplace_back(); + side_t &f = facelist.emplace_back(); f.planenum = PLANENUM_LEAF; f.w.resize(w->size()); @@ -660,7 +671,7 @@ static void AddHullEdge(hullbrush_t *hullbrush, const qvec3d &p1, const qvec3d & ExpandBrush ============= */ -static void ExpandBrush(hullbrush_t *hullbrush, const aabb3d &hull_size, std::vector &facelist) +static void ExpandBrush(hullbrush_t *hullbrush, const aabb3d &hull_size, std::vector &facelist) { int x, s; qbsp_plane_t plane; @@ -755,11 +766,11 @@ LoadBrush Converts a mapbrush to a bsp brush =============== */ -std::optional LoadBrush(const mapentity_t *src, const mapbrush_t *mapbrush, const contentflags_t &contents, +std::optional LoadBrush(const mapentity_t *src, const mapbrush_t *mapbrush, const contentflags_t &contents, const qvec3d &rotate_offset, const rotation_t rottype, const int hullnum) { hullbrush_t hullbrush; - std::vector facelist; + std::vector facelist; // create the faces @@ -796,9 +807,9 @@ std::optional LoadBrush(const mapentity_t *src, const mapbrush_t *mapbr } // create the brush - brush_t brush{}; + bspbrush_t brush{}; brush.contents = contents; - brush.faces = std::move(facelist); + brush.sides = std::move(facelist); brush.bounds = hullbrush.bounds; return brush; } @@ -826,7 +837,7 @@ static void Brush_LoadEntity(mapentity_t *dst, const mapentity_t *src, const int continue; } - std::optional brush = LoadBrush(src, mapbrush, contents, {}, rotation_t::none, 0); + std::optional brush = LoadBrush(src, mapbrush, contents, {}, rotation_t::none, 0); if (brush) { rotate_offset = brush->bounds.centroid(); @@ -946,7 +957,7 @@ static void Brush_LoadEntity(mapentity_t *dst, const mapentity_t *src, const int */ if (hullnum != HULL_COLLISION && contents.is_clip(options.target_game)) { if (hullnum == 0) { - std::optional brush = LoadBrush(src, mapbrush, contents, rotate_offset, rottype, hullnum); + std::optional brush = LoadBrush(src, mapbrush, contents, rotate_offset, rottype, hullnum); if (brush) { dst->bounds += brush->bounds; @@ -996,21 +1007,21 @@ static void Brush_LoadEntity(mapentity_t *dst, const mapentity_t *src, const int contents.set_clips_same_type(clipsametype); contents.illusionary_visblocker = func_illusionary_visblocker; - std::optional brush = LoadBrush(src, mapbrush, contents, rotate_offset, rottype, hullnum); + std::optional brush = LoadBrush(src, mapbrush, contents, rotate_offset, rottype, hullnum); if (!brush) continue; brush->lmshift = lmshift; - for (auto &face : brush->faces) - face.lmshift = { (short) lmshift, (short) lmshift }; + for (auto &face : brush->sides) + face.lmshift = lmshift; if (classname == std::string_view("func_areaportal")) { brush->func_areaportal = const_cast(src); // FIXME: get rid of consts on src in the callers? } options.target_game->count_contents_in_stats(brush->contents, stats); - dst->brushes.push_back(std::make_unique(brush.value())); + dst->brushes.push_back(std::make_unique(brush.value())); dst->bounds += brush->bounds; } @@ -1057,10 +1068,10 @@ void Brush_LoadEntity(mapentity_t *entity, const int hullnum) options.target_game->print_content_stats(stats, "brushes"); } -void brush_t::update_bounds() +void bspbrush_t::update_bounds() { this->bounds = {}; - for (const face_t &face : faces) { + for (const auto &face : sides) { this->bounds = this->bounds.unionWith(face.w.bounds()); } } diff --git a/qbsp/solidbsp.cc b/qbsp/brushbsp.cc similarity index 90% rename from qbsp/solidbsp.cc rename to qbsp/brushbsp.cc index 9837d581..f15c53c3 100644 --- a/qbsp/solidbsp.cc +++ b/qbsp/brushbsp.cc @@ -19,7 +19,7 @@ See file, 'COPYING', for details. */ -#include +#include #include @@ -142,7 +142,7 @@ FaceSide For BSP hueristic ================== */ -static int FaceSide__(const face_t *in, const qbsp_plane_t &split) +static int FaceSide__(const winding_t &w, const qbsp_plane_t &split) { bool have_front, have_back; int i; @@ -151,8 +151,8 @@ static int FaceSide__(const face_t *in, const qbsp_plane_t &split) if (split.type < plane_type_t::PLANE_ANYX) { /* shortcut for axial planes */ - const vec_t *p = &in->w[0][static_cast(split.type)]; - for (i = 0; i < in->w.size(); i++, p += 3) { + const vec_t *p = &w[0][static_cast(split.type)]; + for (i = 0; i < w.size(); i++, p += 3) { if (*p > split.dist + options.epsilon.value()) { if (have_back) return SIDE_ON; @@ -165,8 +165,8 @@ static int FaceSide__(const face_t *in, const qbsp_plane_t &split) } } else { /* sloping planes take longer */ - for (i = 0; i < in->w.size(); i++) { - const vec_t dot = split.distance_to(in->w[i]); + for (i = 0; i < w.size(); i++) { + const vec_t dot = split.distance_to(w[i]); if (dot > options.epsilon.value()) { if (have_back) return SIDE_ON; @@ -196,7 +196,19 @@ inline int FaceSide(const face_t *in, const qbsp_plane_t &split) else if (dist < -in->radius) return SIDE_BACK; else - return FaceSide__(in, split); + return FaceSide__(in->w, split); +} + +inline int FaceSide(const side_t *in, const qbsp_plane_t &split) +{ + vec_t dist = split.distance_to(in->origin); + + if (dist > in->radius) + return SIDE_FRONT; + else if (dist < -in->radius) + return SIDE_BACK; + else + return FaceSide__(in->w, split); } /* @@ -309,13 +321,13 @@ ChooseMidPlaneFromList The clipping hull BSP doesn't worry about avoiding splits ================== */ -static const face_t *ChooseMidPlaneFromList(const std::vector> &brushes, const aabb3d &bounds) +static const side_t *ChooseMidPlaneFromList(const std::vector> &brushes, const aabb3d &bounds) { /* pick the plane that splits the least */ vec_t bestaxialmetric = VECT_MAX; - face_t *bestaxialsurface = nullptr; + side_t *bestaxialsurface = nullptr; vec_t bestanymetric = VECT_MAX; - face_t *bestanysurface = nullptr; + side_t *bestanysurface = nullptr; for (int pass = 0; pass < 2; pass++) { for (auto &brush : brushes) { @@ -323,7 +335,7 @@ static const face_t *ChooseMidPlaneFromList(const std::vectorfaces) { + for (auto &face : brush->sides) { if (face.onnode) continue; if (!face.visible) { @@ -375,12 +387,12 @@ The real BSP heuristic fixme-brushbsp: prefer splits that include a lot of brush sides? ================== */ -static const face_t *ChoosePlaneFromList(const std::vector> &brushes, const aabb3d &bounds) +static const side_t *ChoosePlaneFromList(const std::vector> &brushes, const aabb3d &bounds) { /* pick the plane that splits the least */ int minsplits = INT_MAX - 1; vec_t bestdistribution = VECT_MAX; - face_t *bestsurface = nullptr; + side_t *bestsurface = nullptr; /* passes: * 0: structural visible @@ -397,7 +409,7 @@ static const face_t *ChoosePlaneFromList(const std::vectorfaces) { + for (auto &face : brush->sides) { if (face.onnode) { continue; } @@ -413,7 +425,7 @@ static const face_t *ChoosePlaneFromList(const std::vectorfaces) { + for (auto &face2 : brush2->sides) { if (face2.planenum == face.planenum || face2.onnode) continue; if (!face2.visible) @@ -477,7 +489,7 @@ returns NULL if the surface list can not be divided any more (a leaf) Called in parallel. ================== */ -static const face_t *SelectPartition(const std::vector> &brushes) +static const side_t *SelectPartition(const std::vector> &brushes) { // calculate a bounding box of the entire surfaceset aabb3d bounds; @@ -569,11 +581,11 @@ BrushMostlyOnSide ================== */ -side_t BrushMostlyOnSide(const brush_t &brush, const qplane3d &plane) +planeside_t BrushMostlyOnSide(const bspbrush_t &brush, const qplane3d &plane) { vec_t max = 0; - side_t side = SIDE_FRONT; - for (auto &face : brush.faces) { + planeside_t side = SIDE_FRONT; + for (auto &face : brush.sides) { for (size_t j = 0; j < face.w.size(); j++) { vec_t d = qv::dot(face.w[j], plane.normal) - plane.dist; if (d > max) { @@ -595,13 +607,13 @@ BrushVolume ================== */ -vec_t BrushVolume(const brush_t &brush) +vec_t BrushVolume(const bspbrush_t &brush) { // grab the first valid point as the corner bool found = false; qvec3d corner; - for (auto &face : brush.faces) { + for (auto &face : brush.sides) { if (face.w.size() > 0) { corner = face.w[0]; found = true; @@ -614,7 +626,7 @@ vec_t BrushVolume(const brush_t &brush) // make tetrahedrons to all other faces vec_t volume = 0; - for (auto &face : brush.faces) { + for (auto &face : brush.sides) { auto plane = Face_Plane(&face); vec_t d = -(qv::dot(corner, plane.normal) - plane.dist); vec_t area = face.w.area(); @@ -635,14 +647,14 @@ input. https://github.com/id-Software/Quake-2-Tools/blob/master/bsp/qbsp3/brushbsp.c#L935 ================ */ -twosided> SplitBrush(std::unique_ptr brush, const qplane3d &split) +twosided> SplitBrush(std::unique_ptr brush, const qplane3d &split) { - twosided> result; + twosided> result; // check all points vec_t d_front = 0; vec_t d_back = 0; - for (auto &face : brush->faces) { + for (auto &face : brush->sides) { 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) @@ -664,7 +676,7 @@ twosided> SplitBrush(std::unique_ptr brush, co // create a new winding from the split plane auto w = std::optional{BaseWindingForPlane(split)}; - for (auto &face : brush->faces) { + for (auto &face : brush->sides) { if (!w) { break; } @@ -673,7 +685,7 @@ twosided> SplitBrush(std::unique_ptr brush, co } if (!w || WindingIsTiny(*w)) { // the brush isn't really split - side_t side = BrushMostlyOnSide(*brush, split); + planeside_t side = BrushMostlyOnSide(*brush, split); if (side == SIDE_FRONT) result.front = std::move(brush); else @@ -692,9 +704,9 @@ twosided> SplitBrush(std::unique_ptr brush, co // start with 2 empty brushes for (int i = 0; i < 2; i++) { - result[i] = std::make_unique(); + result[i] = std::make_unique(); result[i]->original = brush->original; - // fixme-brushbsp: add a brush_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]->lmshift = brush->lmshift; result[i]->func_areaportal = brush->func_areaportal; @@ -702,7 +714,7 @@ twosided> SplitBrush(std::unique_ptr brush, co // split all the current windings - for (const auto &face : brush->faces) { + for (const auto &face : brush->sides) { auto cw = face.w.clip(split, 0 /*PLANESIDE_EPSILON*/); for (size_t j = 0; j < 2; j++) { if (!cw[j]) @@ -716,13 +728,13 @@ twosided> SplitBrush(std::unique_ptr brush, co #endif // add the clipped face to result[j] - face_t faceCopy = face; + side_t faceCopy = face; faceCopy.w = *cw[j]; // fixme-brushbsp: configure any settings on the faceCopy? // Q2 does `cs->tested = false;`, why? - result[j]->faces.push_back(std::move(faceCopy)); + result[j]->sides.push_back(std::move(faceCopy)); } } @@ -740,7 +752,7 @@ twosided> SplitBrush(std::unique_ptr brush, co } } - if (result[i]->faces.size() < 3 || bogus) { + if (result[i]->sides.size() < 3 || bogus) { result[i] = nullptr; } } @@ -762,7 +774,7 @@ twosided> SplitBrush(std::unique_ptr brush, co // add the midwinding to both sides for (int i = 0; i < 2; i++) { - face_t cs{}; + side_t cs{}; const bool brushOnFront = (i == 0); @@ -775,7 +787,7 @@ twosided> SplitBrush(std::unique_ptr brush, co cs.w = brushOnFront ? midwinding.flip() : midwinding; - result[i]->faces.push_back(std::move(cs)); + result[i]->sides.push_back(std::move(cs)); } { @@ -806,7 +818,7 @@ 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> &brushes) +static bool AllDetail(const std::vector> &brushes) { for (auto &brush : brushes) { if (!brush->contents.is_any_detail(options.target_game)) { @@ -826,7 +838,7 @@ original faces that have some fragment inside this leaf. Called in parallel. ================== */ -static void CreateLeaf(std::vector> brushes, node_t *leafnode) +static void CreateLeaf(std::vector> brushes, node_t *leafnode) { leafnode->facelist.clear(); leafnode->planenum = PLANENUM_LEAF; @@ -850,9 +862,9 @@ PartitionBrushes Called in parallel. ================== */ -static void PartitionBrushes(std::vector> brushes, node_t *node) +static void PartitionBrushes(std::vector> brushes, node_t *node) { - face_t *split = const_cast(SelectPartition(brushes)); + auto *split = const_cast(SelectPartition(brushes)); if (split == nullptr) { // this is a leaf node node->planenum = PLANENUM_LEAF; @@ -876,7 +888,7 @@ static void PartitionBrushes(std::vector> brushes, node DivideNodeBounds(node, splitplane); // multiple surfaces, so split all the polysurfaces into front and back lists - std::vector> frontlist, backlist; + std::vector> frontlist, backlist; for (auto &brush : brushes) { // NOTE: we're destroying `brushes` here with the std::move() @@ -885,7 +897,7 @@ static void PartitionBrushes(std::vector> brushes, node // mark faces which were used as a splitter for (auto &brushMaybe : frags) { if (brushMaybe) { - for (auto &face : brushMaybe->faces) { + for (auto &face : brushMaybe->sides) { if (face.planenum == split->planenum) { face.onnode = true; } @@ -894,13 +906,13 @@ static void PartitionBrushes(std::vector> brushes, node } if (frags.front) { - if (frags.front->faces.empty()) { + if (frags.front->sides.empty()) { FError("Surface with no faces"); } frontlist.emplace_back(std::move(frags.front)); } if (frags.back) { - if (frags.back->faces.empty()) { + if (frags.back->sides.empty()) { FError("Surface with no faces"); } backlist.emplace_back(std::move(frags.back)); @@ -952,7 +964,7 @@ tree_t *BrushBSP(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->sides) { if (side.visible) { ++visible_brush_sides; } else { @@ -977,9 +989,9 @@ tree_t *BrushBSP(mapentity_t *entity, bool midsplit) mapbrushes = entity->brushes.size(); // set the original pointers - std::vector> brushcopies; + std::vector> brushcopies; for (const auto &original : entity->brushes) { - auto copy = std::make_unique(*original); + auto copy = std::make_unique(*original); copy->original = original.get(); brushcopies.push_back(std::move(copy)); } diff --git a/qbsp/csg4.cc b/qbsp/csg4.cc index 6e09688a..4e6194ea 100644 --- a/qbsp/csg4.cc +++ b/qbsp/csg4.cc @@ -23,7 +23,7 @@ #include #include #include -#include +#include #include #include @@ -80,6 +80,16 @@ void UpdateFaceSphere(face_t *in) in->radius = sqrt(in->radius); } +void UpdateFaceSphere(side_t *in) +{ + in->origin = in->w.center(); + in->radius = 0; + for (size_t i = 0; i < in->w.size(); i++) { + in->radius = max(in->radius, qv::distance2(in->w[i], in->origin)); + } + in->radius = sqrt(in->radius); +} + /* ================== SplitFace @@ -131,9 +141,7 @@ face_t *MirrorFace(const face_t *face) { face_t *newface = NewFaceFromFace(face); newface->w = face->w.flip(); - newface->planeside = static_cast(face->planeside ^ 1); - newface->contents.swap(); - newface->lmshift.swap(); + newface->planeside = static_cast(face->planeside ^ 1); return newface; } diff --git a/qbsp/exportobj.cc b/qbsp/exportobj.cc index 5fb7ceac..b5e03561 100644 --- a/qbsp/exportobj.cc +++ b/qbsp/exportobj.cc @@ -73,7 +73,7 @@ static void ExportObjFace(std::ofstream &f, const face_t *face, int *vertcount) } // fixme-brushbsp - fmt::print(f, "usemtl contents{}\n", face->contents[0].native); + fmt::print(f, "usemtl contents{}\n", face->contents.native); f << 'f'; for (int i = 0; i < face->w.size(); i++) { // .obj vertexes start from 1 @@ -122,15 +122,8 @@ void ExportObj_Faces(const std::string &filesuffix, const std::vector &brushes) +void ExportObj_Brushes(const std::string &filesuffix, const std::vector &brushes) { - std::vector faces; - - for (const brush_t *brush : brushes) - for (auto &face : brush->faces) - faces.push_back(&face); - - ExportObj_Faces(filesuffix, faces); } static void ExportObj_Nodes_r(const node_t *node, std::vector *dest) diff --git a/qbsp/map.cc b/qbsp/map.cc index b46db3ca..731fe9dc 100644 --- a/qbsp/map.cc +++ b/qbsp/map.cc @@ -2284,7 +2284,7 @@ WriteBspBrushMap from q3map ================== */ -void WriteBspBrushMap(const fs::path &name, const std::vector> &list) +void WriteBspBrushMap(const fs::path &name, const std::vector> &list) { logging::print("writing {}\n", name); std::ofstream f(name); @@ -2296,7 +2296,7 @@ void WriteBspBrushMap(const fs::path &name, const std::vectorfaces) { + for (auto &face : brush->sides) { // FIXME: Factor out this mess const qbsp_plane_t &plane = map.planes.at(face.planenum); winding_t w = BaseWindingForPlane(face.planeside ? -plane : plane); @@ -2330,16 +2330,16 @@ from q3map */ static void TestExpandBrushes(const mapentity_t *src) { - std::vector> hull1brushes; + std::vector> hull1brushes; for (int i = 0; i < src->nummapbrushes; i++) { const mapbrush_t *mapbrush = &src->mapbrush(i); - std::optional hull1brush = LoadBrush( + std::optional hull1brush = LoadBrush( src, mapbrush, {CONTENTS_SOLID}, {}, rotation_t::none, options.target_game->id == GAME_QUAKE_II ? HULL_COLLISION : 1); if (hull1brush) { hull1brushes.emplace_back( - std::make_unique(std::move(*hull1brush))); + std::make_unique(std::move(*hull1brush))); } } diff --git a/qbsp/merge.cc b/qbsp/merge.cc index af1a8f4d..e76d7abc 100644 --- a/qbsp/merge.cc +++ b/qbsp/merge.cc @@ -70,7 +70,7 @@ static face_t *TryMerge(face_t *f1, face_t *f2) if (!f1->w.size() || !f2->w.size() || f1->planeside != f2->planeside || f1->texinfo != f2->texinfo || /*!f1->contents[0].equals(options.target_game, f2->contents[0]) || !f1->contents[1].equals(options.target_game, f2->contents[1]) || */ - f1->lmshift[0] != f2->lmshift[0] || f1->lmshift[1] != f2->lmshift[1]) + f1->lmshift != f2->lmshift) return NULL; // find a common edge diff --git a/qbsp/outside.cc b/qbsp/outside.cc index f86307c0..aab2bac7 100644 --- a/qbsp/outside.cc +++ b/qbsp/outside.cc @@ -352,7 +352,7 @@ std::vector FindOccupiedClusters(node_t *headnode) static void MarkBrushSidesInvisible(mapentity_t *entity) { for (auto &brush : entity->brushes) { - for (auto &face : brush->faces) { + for (auto &face : brush->sides) { face.visible = false; } } @@ -368,7 +368,7 @@ static void MarkAllBrushSidesVisible_R(node_t *node) } for (auto *brush : node->original_brushes) { - for (auto &side : brush->faces) { + for (auto &side : brush->sides) { side.visible = true; } } @@ -410,7 +410,7 @@ static void MarkVisibleBrushSides_R(node_t *node) // optimized case: just mark the brush sides in the neighbouring // leaf that are coplanar for (auto *brush : neighbour_leaf->original_brushes) { - for (auto &side : brush->faces) { + for (auto &side : brush->sides) { if (side.planenum == portal->planenum) { // 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. diff --git a/qbsp/portals.cc b/qbsp/portals.cc index db5e142e..ad16fb8a 100644 --- a/qbsp/portals.cc +++ b/qbsp/portals.cc @@ -24,7 +24,7 @@ #include #include -#include +#include #include #include @@ -98,7 +98,7 @@ void MakeHeadnodePortals(tree_t *tree) int i, j, n; portal_t *p, *portals[6]; qbsp_plane_t bplanes[6]; - side_t side; + planeside_t side; // pad with some space so there will never be null volume leafs aabb3d bounds = tree->bounds.grow(SIDESPACE); @@ -167,7 +167,7 @@ std::optional BaseWindingForNode(node_t *node) { plane = map.planes.at(np->planenum); - const side_t keep = (np->children[0] == node) ? + const planeside_t keep = (np->children[0] == node) ? SIDE_FRONT : SIDE_BACK; w = w->clip(plane, BASE_WINDING_EPSILON, false)[keep]; @@ -247,7 +247,7 @@ void SplitNodePortals(node_t *node, portalstats_t &stats) portal_t *next_portal = nullptr; for (portal_t *p = node->portals; p ; p = next_portal) { - side_t side; + planeside_t side; if (p->nodes[SIDE_FRONT] == node) side = SIDE_FRONT; else if (p->nodes[SIDE_BACK] == node) @@ -445,7 +445,7 @@ static void FindPortalSide(portal_t *p) return; int planenum = p->onnode->planenum; - face_t *bestside = nullptr; + side_t *bestside = nullptr; float bestdot = 0; for (int j = 0; j < 2; j++) @@ -460,7 +460,7 @@ static void FindPortalSide(portal_t *p) auto *brush = *it; if (!options.target_game->contents_contains(brush->contents, viscontents)) continue; - for (face_t &side : brush->faces) + for (auto &side : brush->sides) { // fixme-brushbsp: port these // if (side.bevel) @@ -537,7 +537,7 @@ void MarkVisibleSides(tree_t *tree, mapentity_t* entity) // clear all the visible flags for (auto &brush : entity->brushes) { - for (auto &face : brush->faces) { + for (auto &face : brush->sides) { face.visible = false; } } diff --git a/qbsp/qbsp.cc b/qbsp/qbsp.cc index 00f1f1fe..c192fefa 100644 --- a/qbsp/qbsp.cc +++ b/qbsp/qbsp.cc @@ -35,7 +35,7 @@ #include #include #include -#include +#include #include #include #include @@ -296,12 +296,12 @@ Adds any additional planes necessary to allow the brush to be expanded against axial bounding boxes ================= */ -static std::vector> AddBrushBevels(const brush_t &b) +static std::vector> AddBrushBevels(const bspbrush_t &b) { // add already-present planes - std::vector> planes; + std::vector> planes; - for (auto &f : b.faces) { + for (auto &f : b.sides) { int32_t planenum = f.planenum; if (f.planeside) { @@ -336,7 +336,7 @@ static std::vector> AddBrushBevels(const brus int32_t planenum = FindPlane(new_plane, nullptr); int32_t outputplanenum = ExportMapPlane(planenum); - planes.emplace_back(outputplanenum, &b.faces.front()); + planes.emplace_back(outputplanenum, &b.sides.front()); } // if the plane is not in it canonical order, swap it @@ -384,11 +384,11 @@ static std::vector> AddBrushBevels(const brus continue; current.dist = qv::dot(w[j], current.normal); - auto it = b.faces.begin(); + auto it = b.sides.begin(); // if all the points on all the sides are // behind this plane, it is a proper edge bevel - for (; it != b.faces.end(); it++) { + for (; it != b.sides.end(); it++) { auto &f = *it; const auto &plane = map.planes.at(f.planenum); qplane3d temp = f.planeside ? -plane : plane; @@ -410,13 +410,13 @@ static std::vector> AddBrushBevels(const brus break; } - if (it != b.faces.end()) + if (it != b.sides.end()) continue; // wasn't part of the outer hull // add this plane int32_t planenum = FindPlane(current, nullptr); int32_t outputplanenum = ExportMapPlane(planenum); - planes.emplace_back(outputplanenum, &b.faces.front()); + planes.emplace_back(outputplanenum, &b.sides.front()); } } } @@ -1019,13 +1019,13 @@ hull sizes */ static void BSPX_Brushes_AddModel( - struct bspxbrushes_s *ctx, int modelnum, std::vector> &brushes) + struct bspxbrushes_s *ctx, int modelnum, std::vector> &brushes) { bspxbrushes_permodel permodel{1, modelnum}; for (auto &b : brushes) { permodel.numbrushes++; - for (auto &f : b->faces) { + for (auto &f : b->sides) { /*skip axial*/ const auto &plane = map.planes.at(f.planenum); if (fabs(plane.normal[0]) == 1 || fabs(plane.normal[1]) == 1 || @@ -1044,7 +1044,7 @@ static void BSPX_Brushes_AddModel( for (auto &b : brushes) { bspxbrushes_perbrush perbrush{}; - for (auto &f : b->faces) { + for (auto &f : b->sides) { /*skip axial*/ const auto &plane = map.planes.at(f.planenum); if (fabs(plane.normal[0]) == 1 || fabs(plane.normal[1]) == 1 || @@ -1086,7 +1086,7 @@ static void BSPX_Brushes_AddModel( str <= perbrush; - for (auto &f : b->faces) { + for (auto &f : b->sides) { /*skip axial*/ const auto &plane = map.planes.at(f.planenum); if (fabs(plane.normal[0]) == 1 || fabs(plane.normal[1]) == 1 || diff --git a/qbsp/surfaces.cc b/qbsp/surfaces.cc index ee50b3fe..a87cb864 100644 --- a/qbsp/surfaces.cc +++ b/qbsp/surfaces.cc @@ -24,7 +24,7 @@ #include #include #include -#include +#include #include #include @@ -39,7 +39,7 @@ static bool ShouldOmitFace(face_t *f) return true; // HACK: to save a few faces, don't output the interior faces of sky brushes - if (f->contents[0].is_sky(options.target_game)) { + if (f->contents.is_sky(options.target_game)) { return true; } @@ -81,7 +81,7 @@ std::list SubdivideFace(face_t *f) // one lightmap block will always be added at the end, for smooth interpolation // engines that do support scaling will support 256*256 blocks (at whatever scale). - lmshift = f->lmshift[0]; + lmshift = f->lmshift; if (lmshift > 4) lmshift = 4; // no bugging out with legacy lighting @@ -292,7 +292,7 @@ Returns a global edge number, possibly negative to indicate a backwards edge. */ inline size_t GetEdge(mapentity_t *entity, const qvec3d &p1, const qvec3d &p2, const face_t *face) { - if (!face->contents[0].is_valid(options.target_game, false)) + if (!face->contents.is_valid(options.target_game, false)) FError("Face with invalid contents"); size_t v1 = GetVertex(p1); @@ -304,7 +304,7 @@ inline size_t GetEdge(mapentity_t *entity, const qvec3d &p1, const qvec3d &p2, c auto it = hashedges.find(edge_hash_key); if (it != hashedges.end()) { for (const int i : it->second) { - if (pEdgeFaces1[i] == NULL && pEdgeFaces0[i]->contents[0].native == face->contents[0].native) { + if (pEdgeFaces1[i] == NULL && pEdgeFaces0[i]->contents.native == face->contents.native) { pEdgeFaces1[i] = face; return -i; } @@ -392,7 +392,7 @@ static void EmitFaceFragment(mapentity_t *entity, face_t *face, face_fragment_t mface_t &out = map.bsp.dfaces.emplace_back(); // emit lmshift - map.exported_lmshifts.push_back(face->lmshift[1]); + map.exported_lmshifts.push_back(face->lmshift); Q_assert(map.bsp.dfaces.size() == map.exported_lmshifts.size()); out.planenum = ExportMapPlane(face->planenum); @@ -458,7 +458,7 @@ static void CountFace(mapentity_t *entity, face_t *f, size_t &facesCount, size_t if (ShouldOmitFace(f)) return; - if (f->lmshift[1] != 4) + if (f->lmshift != 4) map.needslmshifts = true; facesCount++; @@ -597,7 +597,7 @@ see also FindPortalSide which populates p->side */ static face_t *FaceFromPortal(portal_t *p, int pside) { - face_t *side = p->side; + side_t *side = p->side; if (!side) return nullptr; // portal does not bridge different visible contents @@ -605,7 +605,7 @@ static face_t *FaceFromPortal(portal_t *p, int pside) f->texinfo = side->texinfo; f->planenum = side->planenum; - f->planeside = static_cast(pside); + f->planeside = static_cast(pside); f->portal = p; f->lmshift = side->lmshift; @@ -632,15 +632,12 @@ static face_t *FaceFromPortal(portal_t *p, int pside) if (pside) { f->w = p->winding->flip(); - // fixme-brushbsp: was just `f->contents` on qbsp3 - f->contents[0] = p->nodes[1]->contents; - f->contents[1] = p->nodes[0]->contents; + f->contents = p->nodes[1]->contents; } else { f->w = *p->winding; - f->contents[0] = p->nodes[0]->contents; - f->contents[1] = p->nodes[1]->contents; + f->contents = p->nodes[0]->contents; } UpdateFaceSphere(f); diff --git a/qbsp/test_qbsp.cc b/qbsp/test_qbsp.cc index 0c381790..30d5943a 100644 --- a/qbsp/test_qbsp.cc +++ b/qbsp/test_qbsp.cc @@ -351,10 +351,10 @@ TEST_CASE("duplicatePlanes", "[qbsp]") CHECK(0 == worldspawn.brushes.size()); CHECK(6 == worldspawn.mapbrush(0).numfaces); - std::optional brush = + std::optional brush = LoadBrush(&worldspawn, &worldspawn.mapbrush(0), {CONTENTS_SOLID}, {}, rotation_t::none, 0); REQUIRE(std::nullopt != brush); - CHECK(6 == brush->faces.size()); + CHECK(6 == brush->sides.size()); } /**