qbsp: initial ChopBrushes implementation

This commit is contained in:
Eric Wasylishen 2022-04-30 14:33:26 -06:00
parent 019b8cb9ed
commit cb2268efdf
5 changed files with 138 additions and 1 deletions

View File

@ -21,10 +21,13 @@
#pragma once #pragma once
#include <qbsp/brush.hh>
#include <common/qvec.hh> #include <common/qvec.hh>
#include <list> #include <list>
#include <tuple> #include <tuple>
#include <vector>
struct face_t; struct face_t;
@ -33,3 +36,4 @@ face_t *NewFaceFromFace(const face_t *in);
face_t *MirrorFace(const face_t *face); face_t *MirrorFace(const face_t *face);
std::tuple<face_t *, face_t *> SplitFace(face_t *in, const qplane3d &split); std::tuple<face_t *, face_t *> SplitFace(face_t *in, const qplane3d &split);
void UpdateFaceSphere(face_t *in); void UpdateFaceSphere(face_t *in);
std::vector<brush_t> ChopBrushes(const std::vector<brush_t> &input);

View File

@ -21,8 +21,13 @@
#pragma once #pragma once
#include <qbsp/brush.hh>
#include <common/qvec.hh>
#include <atomic> #include <atomic>
#include <list> #include <list>
#include <optional>
extern std::atomic<int> splitnodes; extern std::atomic<int> splitnodes;
@ -32,4 +37,5 @@ class mapentity_t;
void DetailToSolid(node_t *node); void DetailToSolid(node_t *node);
void PruneNodes(node_t *node); void PruneNodes(node_t *node);
twosided<std::optional<brush_t>> SplitBrush(const brush_t &brush, const qplane3d &split);
node_t *SolidBSP(mapentity_t *entity, bool midsplit); node_t *SolidBSP(mapentity_t *entity, bool midsplit);

View File

@ -23,6 +23,7 @@
#include <qbsp/brush.hh> #include <qbsp/brush.hh>
#include <qbsp/csg4.hh> #include <qbsp/csg4.hh>
#include <qbsp/map.hh> #include <qbsp/map.hh>
#include <qbsp/solidbsp.hh>
#include <qbsp/qbsp.hh> #include <qbsp/qbsp.hh>
#include <atomic> #include <atomic>
@ -474,3 +475,126 @@ std::list<face_t *> CSGFace(face_t *srcface, const mapentity_t *srcentity, const
return result; return result;
} }
/*
==================
SubtractBrush
Returns the fragments from a - b
==================
*/
std::vector<brush_t> SubtractBrush(const brush_t& a, const brush_t& b)
{
// first, check if `a` is fully in front of _any_ of b's planes
for (const auto &side : b.faces) {
auto [front, back] = SplitBrush(a, Face_Plane(&side));
if (front && !back) {
// `a` is fully in front of this side of b, so they don't actually intersect
return {a};
}
}
std::vector<brush_t> frontlist;
std::vector<brush_t> unclassified{a};
for (const auto &side : b.faces) {
std::vector<brush_t> new_unclassified;
for (const auto &fragment : unclassified) {
auto [front, back] = SplitBrush(fragment, Face_Plane(&side));
if (front) {
frontlist.push_back(*front);
}
if (back) {
new_unclassified.push_back(*back);
}
}
unclassified = std::move(new_unclassified);
}
return frontlist;
}
/*
==================
BrushGE
Returns a >= b as far as brush clipping
==================
*/
static bool BrushGE(const brush_t& a, const brush_t& b)
{
int32_t a_pri = a.contents.priority(options.target_game);
int32_t b_pri = b.contents.priority(options.target_game);
return a_pri >= b_pri;
}
/*
==================
ChopBrushes
Clips off any overlapping portions of brushes
==================
*/
std::vector<brush_t> ChopBrushes(const std::vector<brush_t>& input)
{
logging::print(logging::flag::PROGRESS, "---- {} ----\n", __func__);
// output vector for the parallel_for
std::vector<std::vector<brush_t>> brush_fragments;
brush_fragments.resize(input.size());
/*
* For each brush, clip away the parts that are inside other brushes.
* Solid brushes override non-solid brushes.
* brush => the brush to be clipped
* clipbrush => the brush we are clipping against
*
* The output of this is a face list for each brush called "outside"
*/
tbb::parallel_for(static_cast<size_t>(0), input.size(), [input, &brush_fragments](const size_t i) {
const auto &brush = input[i];
// the fragments `brush` is chopped into
std::vector<brush_t> brush_result{brush};
for (auto &clipbrush : input) {
if (&brush == &clipbrush) {
continue;
}
if (brush.bounds.disjoint(clipbrush.bounds)) {
continue;
}
if (BrushGE(clipbrush, brush)) {
std::vector<brush_t> new_result;
// clipbrush is stronger. clip all existing fragments to clipbrush
for (const auto &current_fragment : brush_result) {
for (const auto &new_fragment : SubtractBrush(current_fragment, clipbrush)) {
new_result.push_back(new_fragment);
}
}
brush_result = std::move(new_result);
}
}
// save the result
brush_fragments[i] = brush_result;
});
// Non parallel part:
std::vector<brush_t> result;
for (auto &fragment_list : brush_fragments) {
for (auto &fragment : fragment_list) {
result.push_back(std::move(fragment));
}
}
logging::print(logging::flag::STAT, " {:8} brushes\n", input.size());
logging::print(logging::flag::STAT, " {:8} chopped brushes\n", result.size());
return result;
}

View File

@ -30,6 +30,7 @@
#include <common/settings.hh> #include <common/settings.hh>
#include <qbsp/brush.hh> #include <qbsp/brush.hh>
#include <qbsp/csg4.hh>
#include <qbsp/map.hh> #include <qbsp/map.hh>
#include <qbsp/merge.hh> #include <qbsp/merge.hh>
#include <qbsp/portals.hh> #include <qbsp/portals.hh>
@ -653,6 +654,8 @@ static void ProcessEntity(mapentity_t *entity, const int hullnum)
logging::print(logging::flag::PROGRESS, "---- Brush_LoadEntity ----\n"); logging::print(logging::flag::PROGRESS, "---- Brush_LoadEntity ----\n");
auto stats = Brush_LoadEntity(entity, hullnum); auto stats = Brush_LoadEntity(entity, hullnum);
entity->brushes = ChopBrushes(entity->brushes);
// we're discarding the brush // we're discarding the brush
if (discarded_trigger) { if (discarded_trigger) {
entity->brushes.clear(); entity->brushes.clear();

View File

@ -616,7 +616,7 @@ unchanged
https://github.com/id-Software/Quake-2-Tools/blob/master/bsp/qbsp3/brushbsp.c#L935 https://github.com/id-Software/Quake-2-Tools/blob/master/bsp/qbsp3/brushbsp.c#L935
================ ================
*/ */
static twosided<std::optional<brush_t>> SplitBrush(const brush_t &brush, const qplane3d &split) twosided<std::optional<brush_t>> SplitBrush(const brush_t &brush, const qplane3d &split)
{ {
twosided<std::optional<brush_t>> result; twosided<std::optional<brush_t>> result;