From 426668701e1ec28df94e40424ce841536c0be416 Mon Sep 17 00:00:00 2001 From: Jonathan Date: Mon, 8 Aug 2022 21:08:27 -0400 Subject: [PATCH] clip_back/clip_front to match ChopWindingInPlace simplify constructors for winding heap use std::vector again for mapentity_t since it's being copied because of the std::list --- bsputil/decompile.cpp | 8 +- include/common/bitflags.hh | 4 +- include/common/polylib.hh | 148 ++++++++++++++++++++++++++++++++----- include/qbsp/map.hh | 2 +- light/trace_embree.cc | 6 +- qbsp/brush.cc | 2 +- qbsp/map.cc | 4 +- qbsp/portals.cc | 19 ++++- qbsp/qbsp.cc | 2 +- 9 files changed, 158 insertions(+), 37 deletions(-) diff --git a/bsputil/decompile.cpp b/bsputil/decompile.cpp index e1d3e693..0a132211 100644 --- a/bsputil/decompile.cpp +++ b/bsputil/decompile.cpp @@ -244,10 +244,8 @@ void RemoveRedundantPlanes(std::vector &planes) // get flipped plane // frees winding. - auto clipped = winding->clip(-plane2); - // discard the back, continue clipping the front part - winding = clipped[0]; + winding = winding->clip_front(-plane2); // check if everything was clipped away if (!winding) @@ -607,9 +605,7 @@ static decomp_brush_t BuildInitialBrush_Q2( break; // FIXME: epsilon is a lot larger than what qbsp uses - auto [front, back] = winding->clip(plane2, DEFAULT_ON_EPSILON, true); - - winding = back; + winding = winding->clip_back(plane2, DEFAULT_ON_EPSILON, true); } if (!winding) diff --git a/include/common/bitflags.hh b/include/common/bitflags.hh index 2b9be1dd..3a4206c1 100644 --- a/include/common/bitflags.hh +++ b/include/common/bitflags.hh @@ -39,10 +39,10 @@ public: constexpr bitflags(const Enum &enumValue) : _bits(static_cast(enumValue)) { } constexpr bitflags(const bitflags ©) = default; - constexpr bitflags(bitflags &&move) = default; + constexpr bitflags(bitflags &&move) noexcept = default; constexpr bitflags &operator=(const bitflags ©) = default; - constexpr bitflags &operator=(bitflags &&move) = default; + constexpr bitflags &operator=(bitflags &&move) noexcept = default; inline explicit operator bool() const { return _bits.any(); } diff --git a/include/common/polylib.hh b/include/common/polylib.hh index 1cca5ab9..098e373f 100644 --- a/include/common/polylib.hh +++ b/include/common/polylib.hh @@ -218,16 +218,14 @@ public: // construct winding from range. // iterators must have operator+ and operator-. template, int> = 0> - inline winding_storage_heap_t(Iter begin, Iter end) : winding_storage_heap_t(end - begin) + inline winding_storage_heap_t(Iter begin, Iter end) : values(begin, end) { - std::copy_n(begin, size(), values.data()); } // copy constructor; uses optimized method of copying // data over. - inline winding_storage_heap_t(const winding_storage_heap_t ©) : winding_storage_heap_t(copy.size()) + inline winding_storage_heap_t(const winding_storage_heap_t ©) : values(copy.values) { - std::copy_n(copy.values.data(), copy.size(), values.data()); } // move constructor @@ -259,21 +257,11 @@ public: inline qvec3d &at(const size_t &index) { -#ifdef _DEBUG - if (index >= count) - throw std::invalid_argument("index"); -#endif - return values[index]; } inline const qvec3d &at(const size_t &index) const { -#ifdef _DEBUG - if (index >= count) - throw std::invalid_argument("index"); -#endif - return values[index]; } @@ -1050,12 +1038,138 @@ public: results[SIDE_BACK].push_back(mid); } - if (results[SIDE_FRONT].size() > MAX_POINTS_ON_WINDING || results[SIDE_BACK].size() > MAX_POINTS_ON_WINDING) - FError("MAX_POINTS_ON_WINDING"); - return {std::move(results[SIDE_FRONT]), std::move(results[SIDE_BACK])}; } + /* + ================== + clip_front + + The same as the above, except it will avoid allocating the front + if it doesn't need to be modified; destroys *this if it does end up + allocating a new winding. + + Cheaper than clip(...)[SIDE_FRONT] + ================== + */ + std::optional clip_front( + const qplane3d &plane, const vec_t &on_epsilon = DEFAULT_ON_EPSILON, const bool &keepon = false) + { + vec_t *dists = (vec_t *)alloca(sizeof(vec_t) * (size() + 1)); + planeside_t *sides = (planeside_t *)alloca(sizeof(planeside_t) * (size() + 1)); + + std::array counts = calc_sides(plane, dists, sides, on_epsilon); + + if (keepon && !counts[SIDE_FRONT] && !counts[SIDE_BACK]) + return std::move(*this); + + if (!counts[SIDE_FRONT]) + return std::nullopt; + else if (!counts[SIDE_BACK]) + return std::move(*this); + + winding_base_t result; + result.reserve(size() + 4); + + for (size_t i = 0; i < size(); i++) { + const qvec3d &p1 = at(i); + + if (sides[i] == SIDE_ON) { + result.push_back(p1); + continue; + } else if (sides[i] == SIDE_FRONT) { + result.push_back(p1); + } + + if (sides[i + 1] == SIDE_ON || sides[i + 1] == sides[i]) + continue; + + /* generate a split point */ + const qvec3d &p2 = at((i + 1) % size()); + + vec_t dot = dists[i] / (dists[i] - dists[i + 1]); + qvec3d mid; + + for (size_t j = 0; j < 3; j++) { /* avoid round off error when possible */ + if (plane.normal[j] == 1) + mid[j] = plane.dist; + else if (plane.normal[j] == -1) + mid[j] = -plane.dist; + else + mid[j] = p1[j] + dot * (p2[j] - p1[j]); + } + + result.push_back(mid); + } + + return std::move(result); + } + + + /* + ================== + clip_back + + The same as the above, except it will avoid allocating the front + if it doesn't need to be modified; destroys *this if it does end up + allocating a new winding. + + Cheaper than clip(...)[SIDE_BACK] + ================== + */ + std::optional clip_back( + 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) * (size() + 1)); + planeside_t *sides = (planeside_t *)alloca(sizeof(planeside_t) * (size() + 1)); + + std::array counts = calc_sides(plane, dists, sides, on_epsilon); + + if (keepon && !counts[SIDE_FRONT] && !counts[SIDE_BACK]) + return std::nullopt; + + if (!counts[SIDE_FRONT]) + return std::move(*this); + else if (!counts[SIDE_BACK]) + return std::nullopt; + + winding_base_t result; + result.reserve(size() + 4); + + for (size_t i = 0; i < size(); i++) { + const qvec3d &p1 = at(i); + + if (sides[i] == SIDE_ON) { + result.push_back(p1); + continue; + } else if (sides[i] == SIDE_BACK) { + result.push_back(p1); + } + + if (sides[i + 1] == SIDE_ON || sides[i + 1] == sides[i]) + continue; + + /* generate a split point */ + const qvec3d &p2 = at((i + 1) % size()); + + vec_t dot = dists[i] / (dists[i] - dists[i + 1]); + qvec3d mid; + + for (size_t j = 0; j < 3; j++) { /* avoid round off error when possible */ + if (plane.normal[j] == 1) + mid[j] = plane.dist; + else if (plane.normal[j] == -1) + mid[j] = -plane.dist; + else + mid[j] = p1[j] + dot * (p2[j] - p1[j]); + } + + result.push_back(mid); + } + + return std::move(result); + } + void dice(vec_t subdiv, std::function save_fn) { if (!size()) diff --git a/include/qbsp/map.hh b/include/qbsp/map.hh index 62aeb685..f6c9573e 100644 --- a/include/qbsp/map.hh +++ b/include/qbsp/map.hh @@ -104,7 +104,7 @@ public: qvec3d origin{}; rotation_t rotation; - std::list mapbrushes; + std::vector mapbrushes; size_t numboxbevels = 0; size_t numedgebevels = 0; diff --git a/light/trace_embree.cc b/light/trace_embree.cc index b1603746..df3d2acc 100644 --- a/light/trace_embree.cc +++ b/light/trace_embree.cc @@ -399,11 +399,9 @@ static void Leaf_MakeFaces( for (const qplane3d &plane2 : planes) { if (&plane2 == &plane) continue; - - auto clipped = winding->clip(plane2); - + // discard the back, continue clipping the front part - winding = clipped[0]; + winding = winding->clip_front(plane2); // check if everything was clipped away if (!winding) diff --git a/qbsp/brush.cc b/qbsp/brush.cc index 3da0e576..ce7d2858 100644 --- a/qbsp/brush.cc +++ b/qbsp/brush.cc @@ -227,7 +227,7 @@ bool CreateBrushWindings(bspbrush_t *brush) if (brush->sides[j].bevel) continue; const qplane3d &plane = map.planes[brush->sides[j].planenum ^ 1]; - w = w->clip(plane, qbsp_options.epsilon.value(), false)[SIDE_FRONT]; // CLIP_EPSILON); + w = w->clip_front(plane, qbsp_options.epsilon.value(), false); // CLIP_EPSILON); } if (w) { diff --git a/qbsp/map.cc b/qbsp/map.cc index cc1878fe..55eb1b06 100644 --- a/qbsp/map.cc +++ b/qbsp/map.cc @@ -2227,7 +2227,7 @@ inline void CalculateBrushBounds(mapbrush_t &ob) continue; } const auto &plane = map.get_plane(ob.faces[j].planenum ^ 1); - w = w->clip(plane, 0)[SIDE_FRONT]; //CLIP_EPSILON); + w = w->clip_front(plane, 0); //CLIP_EPSILON); } if (w) { @@ -2288,6 +2288,8 @@ void ProcessMapBrushes() } num_removed++; + // this is kinda slow but since most origin brushes are in + // small brush models this won't matter much in practice it = entity.mapbrushes.erase(it); entity.rotation = rotation_t::origin_brush; continue; diff --git a/qbsp/portals.cc b/qbsp/portals.cc index e6f18b52..7b7fe8a3 100644 --- a/qbsp/portals.cc +++ b/qbsp/portals.cc @@ -168,10 +168,17 @@ std::list> MakeHeadnodePortals(tree_t *tree) // clip the basewindings by all the other planes for (i = 0; i < 6; i++) { + winding_t &w = *portals[i]->winding.get(); + for (j = 0; j < 6; j++) { if (j == i) continue; - portals[i]->winding = std::make_unique(*portals[i]->winding->clip(bplanes[j], qbsp_options.epsilon.value(), true)[SIDE_FRONT]); + + if (auto w2 = w.clip_front(bplanes[j], qbsp_options.epsilon.value(), true)) { + w = std::move(*w2); + } else { + FError("portal winding clipped away"); + } } } @@ -201,9 +208,12 @@ static std::optional BaseWindingForNode(const node_t *node) // clip by all the parents for (auto *np = node->parent; np && w;) { - const planeside_t keep = (np->children[0].get() == node) ? SIDE_FRONT : SIDE_BACK; - w = w->clip(np->get_plane(), BASE_WINDING_EPSILON, false)[keep]; + if (np->children[0].get() == node) { + w = w->clip_front(np->get_plane(), BASE_WINDING_EPSILON, false); + } else { + w = w->clip_back(np->get_plane(), BASE_WINDING_EPSILON, false); + } node = np; np = np->parent; @@ -240,7 +250,8 @@ std::unique_ptr MakeNodePortal(node_t *node, const std::listclip(plane, 0.1, false)[SIDE_FRONT]; + // fixme-brushbsp: magic number + w = w->clip_front(plane, 0.1, false); } if (!w) { diff --git a/qbsp/qbsp.cc b/qbsp/qbsp.cc index b2bda899..4fbedf9b 100644 --- a/qbsp/qbsp.cc +++ b/qbsp/qbsp.cc @@ -651,7 +651,7 @@ hull sizes */ static void BSPX_Brushes_AddModel( - struct bspxbrushes_s *ctx, int modelnum, const std::list &brushes) + struct bspxbrushes_s *ctx, int modelnum, const std::vector &brushes) { bspxbrushes_permodel permodel{1, modelnum};