From 146f20f6774f2eac122d92ce04776f907787b899 Mon Sep 17 00:00:00 2001 From: Jonathan Date: Wed, 27 Jul 2022 21:45:59 -0400 Subject: [PATCH] qbsp_plane_t is now used by other structures, and contains a faster epsilonEqual; since the majority of planes (especially nodes) are going to be axial, there's no need to use the slower full comparison when we can check if the axial component is ready. --- include/qbsp/map.hh | 65 ------------------ include/qbsp/portals.hh | 2 +- include/qbsp/qbsp.hh | 141 +++++++++++++++++++++++++++++++++++++++- qbsp/brush.cc | 11 ++-- qbsp/map.cc | 1 + qbsp/portals.cc | 4 +- qbsp/writebsp.cc | 2 +- 7 files changed, 151 insertions(+), 75 deletions(-) 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);