diff --git a/include/qbsp/map.hh b/include/qbsp/map.hh index 1c68a68d..dcdddc8b 100644 --- a/include/qbsp/map.hh +++ b/include/qbsp/map.hh @@ -37,71 +37,6 @@ struct bspbrush_t; -struct qbsp_plane_t : qplane3d -{ - plane_type_t type = plane_type_t::PLANE_INVALID; - - [[nodiscard]] constexpr qbsp_plane_t operator-() const { return {qplane3d::operator-(), type}; } - - // create a qbsp_plane_t from a plane. - // if flip is true, the returned plane will only be positive. - static inline qbsp_plane_t from_plane(const qplane3d &plane, bool flip, bool &was_flipped) - { - was_flipped = false; - qbsp_plane_t p { plane }; - - for (size_t i = 0; i < 3; i++) { - if (p.normal[i] == 1.0) { - p.normal[(i + 1) % 3] = 0; - p.normal[(i + 2) % 3] = 0; - p.type = (i == 0 ? plane_type_t::PLANE_X : i == 1 ? plane_type_t::PLANE_Y : plane_type_t::PLANE_Z); - return p; - } - if (p.normal[i] == -1.0) { - if (flip) { - p.normal[i] = 1.0; - p.dist = -p.dist; - was_flipped = true; - } - p.normal[(i + 1) % 3] = 0; - p.normal[(i + 2) % 3] = 0; - p.type = (i == 0 ? plane_type_t::PLANE_X : i == 1 ? plane_type_t::PLANE_Y : plane_type_t::PLANE_Z); - return p; - } - } - - vec_t ax = fabs(p.normal[0]); - vec_t ay = fabs(p.normal[1]); - vec_t az = fabs(p.normal[2]); - - size_t nearest; - - if (ax >= ay && ax >= az) { - nearest = 0; - p.type = plane_type_t::PLANE_ANYX; - } else if (ay >= ax && ay >= az) { - nearest = 1; - p.type = plane_type_t::PLANE_ANYY; - } else { - nearest = 2; - p.type = plane_type_t::PLANE_ANYZ; - } - - if (flip && p.normal[nearest] < 0) { - was_flipped = true; - return -p; - } - - return p; - } - - static inline qbsp_plane_t from_plane(const qplane3d &plane, bool flip = false) - { - bool unused; - return from_plane(plane, flip, unused); - } -}; - struct mapface_t { qbsp_plane_t plane{}; diff --git a/include/qbsp/portals.hh b/include/qbsp/portals.hh index 07198f2d..b8017af3 100644 --- a/include/qbsp/portals.hh +++ b/include/qbsp/portals.hh @@ -33,7 +33,7 @@ struct tree_t; struct portal_t { - qplane3d plane; + qbsp_plane_t plane; node_t *onnode; // nullptr = portal to the outside of the world (one of six sides of a box) node_t *nodes[2]; // [0] = front side of planenum portal_t *next[2]; // [0] = next portal in nodes[0]'s list of portals diff --git a/include/qbsp/qbsp.hh b/include/qbsp/qbsp.hh index e7be60c8..c5b1e3a7 100644 --- a/include/qbsp/qbsp.hh +++ b/include/qbsp/qbsp.hh @@ -379,6 +379,145 @@ struct face_t portal_t *portal; }; + + +struct qbsp_plane_t : qplane3d +{ + plane_type_t type = plane_type_t::PLANE_INVALID; + + qbsp_plane_t() = default; + qbsp_plane_t(const qbsp_plane_t &) = default; + constexpr qbsp_plane_t(const qplane3d &plane, const plane_type_t &type) noexcept : + qplane3d(plane), + type(type) + { + } + inline qbsp_plane_t(const qplane3d &plane) noexcept : + qbsp_plane_t(plane, calculate_type(plane)) + { + } + + qbsp_plane_t &operator=(const qbsp_plane_t &) = default; + inline qbsp_plane_t &operator=(const qplane3d &plane) noexcept + { + return *this = from_plane(plane); + } + + [[nodiscard]] constexpr qbsp_plane_t operator-() const { return {qplane3d::operator-(), type}; } + + // create a qbsp_plane_t from a plane. + // if flip is true, the returned plane will only be positive. + static inline qbsp_plane_t from_plane(const qplane3d &plane, bool flip, bool &was_flipped) noexcept + { + was_flipped = false; + qbsp_plane_t p { plane }; + + for (size_t i = 0; i < 3; i++) { + if (p.normal[i] == 1.0) { + p.normal[(i + 1) % 3] = 0; + p.normal[(i + 2) % 3] = 0; + p.type = (i == 0 ? plane_type_t::PLANE_X : i == 1 ? plane_type_t::PLANE_Y : plane_type_t::PLANE_Z); + return p; + } + if (p.normal[i] == -1.0) { + if (flip) { + p.normal[i] = 1.0; + p.dist = -p.dist; + was_flipped = true; + } + p.normal[(i + 1) % 3] = 0; + p.normal[(i + 2) % 3] = 0; + p.type = (i == 0 ? plane_type_t::PLANE_X : i == 1 ? plane_type_t::PLANE_Y : plane_type_t::PLANE_Z); + return p; + } + } + + vec_t ax = fabs(p.normal[0]); + vec_t ay = fabs(p.normal[1]); + vec_t az = fabs(p.normal[2]); + + size_t nearest; + + if (ax >= ay && ax >= az) { + nearest = 0; + p.type = plane_type_t::PLANE_ANYX; + } else if (ay >= ax && ay >= az) { + nearest = 1; + p.type = plane_type_t::PLANE_ANYY; + } else { + nearest = 2; + p.type = plane_type_t::PLANE_ANYZ; + } + + if (flip && p.normal[nearest] < 0) { + was_flipped = true; + return -p; + } + + return p; + } + + static inline qbsp_plane_t from_plane(const qplane3d &plane, bool flip = false) + { + bool unused; + return from_plane(plane, flip, unused); + } + + static inline plane_type_t calculate_type(const qplane3d &p) + { + for (size_t i = 0; i < 3; i++) { + if (p.normal[i] == 1.0 || p.normal[i] == -1.0) { + return (i == 0 ? plane_type_t::PLANE_X : i == 1 ? plane_type_t::PLANE_Y : plane_type_t::PLANE_Z); + } + } + + vec_t ax = fabs(p.normal[0]); + vec_t ay = fabs(p.normal[1]); + vec_t az = fabs(p.normal[2]); + + if (ax >= ay && ax >= az) { + return plane_type_t::PLANE_ANYX; + } else if (ay >= ax && ay >= az) { + return plane_type_t::PLANE_ANYY; + } else { + return plane_type_t::PLANE_ANYZ; + } + } +}; + +namespace qv +{ +// faster version of epsilonEqual for BSP planes +// which have a bit more info in them +[[nodiscard]] inline bool epsilonEqual(const qbsp_plane_t &p1, const qbsp_plane_t &p2, vec_t normalEpsilon = NORMAL_EPSILON, vec_t distEpsilon = DIST_EPSILON) +{ + Q_assert(p1.type != plane_type_t::PLANE_INVALID); + Q_assert(p2.type != plane_type_t::PLANE_INVALID); + + // axial planes will never match on normal, so we can skip that check entirely + if (p1.type < plane_type_t::PLANE_ANYX && p2.type < plane_type_t::PLANE_ANYX) { + // if we aren't the same type, we definitely aren't equal + if (p1.type != p2.type) { + return false; + } else if (p1.normal[static_cast(p1.type)] != p2.normal[static_cast(p2.type)]) { + // axials will always be only 1 or -1 + return false; + } + + // check dist + return epsilonEqual(p1.dist, p2.dist, distEpsilon); + } + + // check dist + if (!epsilonEqual(p1.dist, p2.dist, distEpsilon)) { + return false; + } + + // check normal + return epsilonEqual(p1.normal, p2.normal, normalEpsilon); +} +}; // namespace qv + // there is a node_t structure for every node and leaf in the bsp tree struct bspbrush_t; @@ -394,7 +533,7 @@ struct node_t bool is_leaf = false; // information for decision nodes - qplane3d plane; // decision node only + qbsp_plane_t plane; // decision node only int firstface; // decision node only int numfaces; // decision node only twosided> children; // children[0] = front side, children[1] = back side of plane. only valid for decision nodes diff --git a/qbsp/brush.cc b/qbsp/brush.cc index f4456091..e6f578e6 100644 --- a/qbsp/brush.cc +++ b/qbsp/brush.cc @@ -374,7 +374,7 @@ This is done by brute force, and could easily get a lot faster if anyone cares. AddBrushPlane ============= */ -static void AddBrushPlane(hullbrush_t *hullbrush, const qplane3d &plane) +static void AddBrushPlane(hullbrush_t *hullbrush, const qbsp_plane_t &plane) { vec_t len = qv::length(plane.normal); @@ -382,8 +382,7 @@ static void AddBrushPlane(hullbrush_t *hullbrush, const qplane3d &plane) FError("invalid normal (vector length {:.4})", len); for (auto &mapface : hullbrush->faces) { - if (qv::epsilonEqual(mapface.plane.normal, plane.normal, EQUAL_EPSILON) && - fabs(mapface.plane.dist - plane.dist) < qbsp_options.epsilon.value()) + if (qv::epsilonEqual(mapface.plane, plane, EQUAL_EPSILON, qbsp_options.epsilon.value())) return; } @@ -404,7 +403,7 @@ Adds the given plane to the brush description if all of the original brush vertexes can be put on the front side ============= */ -static void TestAddPlane(hullbrush_t *hullbrush, qplane3d &plane) +static void TestAddPlane(hullbrush_t *hullbrush, qbsp_plane_t &plane) { vec_t d; int points_front, points_back; @@ -483,7 +482,7 @@ static void AddHullEdge(hullbrush_t *hullbrush, const qvec3d &p1, const qvec3d & { int pt1, pt2; int a, b, c, d, e; - qplane3d plane; + qbsp_plane_t plane; vec_t length; pt1 = AddHullPoint(hullbrush, p1, hull_size); @@ -523,6 +522,7 @@ static void AddHullEdge(hullbrush_t *hullbrush, const qvec3d &p1, const qvec3d & planeorg[b] += hull_size[d][b]; planeorg[c] += hull_size[e][c]; plane.dist = qv::dot(planeorg, plane.normal); + plane.type = qbsp_plane_t::calculate_type(plane); TestAddPlane(hullbrush, plane); } } @@ -575,6 +575,7 @@ static void ExpandBrush(hullbrush_t *hullbrush, const aabb3d &hull_size, std::ve plane.dist = -hullbrush->bounds.mins()[x] + -hull_size[0][x]; else plane.dist = hullbrush->bounds.maxs()[x] + hull_size[1][x]; + plane.type = qbsp_plane_t::calculate_type(plane); AddBrushPlane(hullbrush, plane); } diff --git a/qbsp/map.cc b/qbsp/map.cc index bda68cb1..b12ff60b 100644 --- a/qbsp/map.cc +++ b/qbsp/map.cc @@ -1531,6 +1531,7 @@ bool mapface_t::set_planepts(const std::array &pts) plane.normal = qv::normalize(qv::cross(ab, cb), length); plane.dist = qv::dot(planepts[1], plane.normal); + plane.type = qbsp_plane_t::calculate_type(plane); return length >= NORMAL_EPSILON; } diff --git a/qbsp/portals.cc b/qbsp/portals.cc index 23085ab3..ade67ad6 100644 --- a/qbsp/portals.cc +++ b/qbsp/portals.cc @@ -163,7 +163,7 @@ void MakeHeadnodePortals(tree_t *tree) { int i, j, n; portal_t *p, *portals[6]; - qbsp_plane_t bplanes[6]; + qplane3d bplanes[6]; // pad with some space so there will never be null volume leafs aabb3d bounds = tree->bounds.grow(SIDESPACE); @@ -726,7 +726,7 @@ static void FindPortalSide(portal_t *p) // 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}; float bestdot = 0; - const qplane3d &p1 = p->onnode->plane; + const qbsp_plane_t &p1 = p->onnode->plane; // check brushes on both sides of the portal for (int j = 0; j < 2; j++) diff --git a/qbsp/writebsp.cc b/qbsp/writebsp.cc index 731cfa8f..a9c9e058 100644 --- a/qbsp/writebsp.cc +++ b/qbsp/writebsp.cc @@ -64,7 +64,7 @@ static mapplane_t &NewPlane(const qplane3d &plane) return added_plane; } -static mapplane_t &FindPlane(const qplane3d &plane) +static mapplane_t &FindPlane(const qbsp_plane_t &plane) { for (int i : map.planehash[plane_hash_fn(plane)]) { mapplane_t &p = map.planes.at(i);