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
This commit is contained in:
Jonathan 2022-08-08 21:08:27 -04:00
parent 8b7a1c21c9
commit 426668701e
9 changed files with 158 additions and 37 deletions

View File

@ -244,10 +244,8 @@ void RemoveRedundantPlanes(std::vector<T> &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)

View File

@ -39,10 +39,10 @@ public:
constexpr bitflags(const Enum &enumValue) : _bits(static_cast<type>(enumValue)) { }
constexpr bitflags(const bitflags &copy) = default;
constexpr bitflags(bitflags &&move) = default;
constexpr bitflags(bitflags &&move) noexcept = default;
constexpr bitflags &operator=(const bitflags &copy) = default;
constexpr bitflags &operator=(bitflags &&move) = default;
constexpr bitflags &operator=(bitflags &&move) noexcept = default;
inline explicit operator bool() const { return _bits.any(); }

View File

@ -218,16 +218,14 @@ public:
// construct winding from range.
// iterators must have operator+ and operator-.
template<typename Iter, std::enable_if_t<is_iterator_v<Iter>, 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 &copy) : winding_storage_heap_t(copy.size())
inline winding_storage_heap_t(const winding_storage_heap_t &copy) : 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<winding_base_t> 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<size_t, SIDE_TOTAL> 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<winding_base_t> 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<size_t, SIDE_TOTAL> 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<void(winding_base_t &)> save_fn)
{
if (!size())

View File

@ -104,7 +104,7 @@ public:
qvec3d origin{};
rotation_t rotation;
std::list<mapbrush_t> mapbrushes;
std::vector<mapbrush_t> mapbrushes;
size_t numboxbevels = 0;
size_t numedgebevels = 0;

View File

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

View File

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

View File

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

View File

@ -168,10 +168,17 @@ std::list<std::unique_ptr<buildportal_t>> 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<winding_t>(*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<winding_t> 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<buildportal_t> MakeNodePortal(node_t *node, const std::list<std:
Error("CutNodePortals_r: mislinked portal");
}
w = w->clip(plane, 0.1, false)[SIDE_FRONT];
// fixme-brushbsp: magic number
w = w->clip_front(plane, 0.1, false);
}
if (!w) {

View File

@ -651,7 +651,7 @@ hull sizes
*/
static void BSPX_Brushes_AddModel(
struct bspxbrushes_s *ctx, int modelnum, const std::list<mapbrush_t> &brushes)
struct bspxbrushes_s *ctx, int modelnum, const std::vector<mapbrush_t> &brushes)
{
bspxbrushes_permodel permodel{1, modelnum};