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.

This commit is contained in:
Jonathan 2022-07-27 21:45:59 -04:00
parent c5d0c4dc67
commit 146f20f677
7 changed files with 151 additions and 75 deletions

View File

@ -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{};

View File

@ -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

View File

@ -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<int32_t>(p1.type)] != p2.normal[static_cast<int32_t>(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<std::unique_ptr<node_t>> children; // children[0] = front side, children[1] = back side of plane. only valid for decision nodes

View File

@ -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);
}

View File

@ -1531,6 +1531,7 @@ bool mapface_t::set_planepts(const std::array<qvec3d, 3> &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;
}

View File

@ -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++)

View File

@ -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);