qbsp: planning outside filling approach, wip
This commit is contained in:
parent
449b76db70
commit
edad06cc5a
|
|
@ -323,14 +323,17 @@ struct face_t : face_fragment_t
|
|||
|
||||
mapentity_t *src_entity; // source entity
|
||||
face_t *original; // face on node
|
||||
bool touchesOccupiedLeaf; // internal use in outside.cc
|
||||
qvec3d origin;
|
||||
vec_t radius;
|
||||
|
||||
// filled by TJunc
|
||||
std::vector<face_fragment_t> fragments;
|
||||
|
||||
// fixme-brushbsp: move to a brush_side_t struct
|
||||
bool onnode; // has this face been used as a BSP node plane yet?
|
||||
bool visible; // can any part of this side be seen from non-void parts of the level?
|
||||
// non-visible means we can discard the brush side
|
||||
// (avoiding generating a BSP spit, so expanding it outwards)
|
||||
};
|
||||
|
||||
// there is a node_t structure for every node and leaf in the bsp tree
|
||||
|
|
|
|||
131
qbsp/outside.cc
131
qbsp/outside.cc
|
|
@ -116,6 +116,7 @@ static bool Portal_Passable(const portal_t *p)
|
|||
|
||||
// fixme-brushbsp: confirm, why was this not needed before?
|
||||
// detail separators are treated as non-opaque because detail doesn't block vis
|
||||
// fixme-brushbsp: should probably move to node_t::opaque()
|
||||
if (l->detail_separator) {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -322,77 +323,24 @@ std::vector<node_t *> FindOccupiedClusters(node_t *headnode)
|
|||
|
||||
/*
|
||||
==================
|
||||
ResetFacesTouchingOccupiedLeafs
|
||||
|
||||
Set f->touchesOccupiedLeaf=false on all faces.
|
||||
==================
|
||||
*/
|
||||
static void ResetFacesTouchingOccupiedLeafs(node_t *node)
|
||||
{
|
||||
if (node->planenum == PLANENUM_LEAF) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (face_t *face : node->facelist) {
|
||||
face->touchesOccupiedLeaf = false;
|
||||
}
|
||||
|
||||
ResetFacesTouchingOccupiedLeafs(node->children[0]);
|
||||
ResetFacesTouchingOccupiedLeafs(node->children[1]);
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
MarkFacesTouchingOccupiedLeafs
|
||||
MarkVisibleBrushSides
|
||||
|
||||
Set f->touchesOccupiedLeaf=true on faces that are touching occupied leafs
|
||||
==================
|
||||
*/
|
||||
static void MarkFacesTouchingOccupiedLeafs(node_t *node)
|
||||
static void MarkVisibleBrushSides(node_t *node)
|
||||
{
|
||||
if (node->planenum != PLANENUM_LEAF) {
|
||||
MarkFacesTouchingOccupiedLeafs(node->children[0]);
|
||||
MarkFacesTouchingOccupiedLeafs(node->children[1]);
|
||||
MarkVisibleBrushSides(node->children[0]);
|
||||
MarkVisibleBrushSides(node->children[1]);
|
||||
return;
|
||||
}
|
||||
|
||||
// visit the leaf
|
||||
|
||||
if (node->outside_distance == -1) {
|
||||
// This is an occupied leaf, so we need to keep all of the faces touching it.
|
||||
for (auto &markface : node->markfaces) {
|
||||
markface->touchesOccupiedLeaf = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
ClearOutFaces
|
||||
|
||||
Deletes (by setting f->w.numpoints=0) faces in solid nodes
|
||||
==================
|
||||
*/
|
||||
static void ClearOutFaces(node_t *node)
|
||||
{
|
||||
if (node->planenum != PLANENUM_LEAF) {
|
||||
ClearOutFaces(node->children[0]);
|
||||
ClearOutFaces(node->children[1]);
|
||||
return;
|
||||
}
|
||||
|
||||
// visit the leaf
|
||||
if (!node->contents.is_solid(options.target_game)) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto &markface : node->markfaces) {
|
||||
// NOTE: This is how faces are deleted here, kind of ugly
|
||||
markface->w.clear();
|
||||
}
|
||||
|
||||
// FIXME: Shouldn't be needed here
|
||||
node->facelist = {};
|
||||
// fixme-brushbsp: do a flood fill from all empty leafs.
|
||||
// when we reach a portal to solid, look for a brush side with the same
|
||||
// planenum. mark it as 'visible'.
|
||||
}
|
||||
|
||||
static void OutLeafsToSolid_r(node_t *node, int *outleafs_count)
|
||||
|
|
@ -411,20 +359,7 @@ static void OutLeafsToSolid_r(node_t *node, int *outleafs_count)
|
|||
if (node->contents.is_solid(options.target_game) || node->contents.is_sky(options.target_game))
|
||||
return;
|
||||
|
||||
// Now check all faces touching the leaf. If any of them are partially going into the occupied part of the map,
|
||||
// don't fill the leaf (see comment in FillOutside).
|
||||
bool skipFill = false;
|
||||
for (auto &markface : node->markfaces) {
|
||||
if (markface->touchesOccupiedLeaf) {
|
||||
skipFill = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (skipFill) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Finally, we can fill it in as void.
|
||||
// Finally, we can fill it in as void.
|
||||
node->contents = options.target_game->create_solid_contents();
|
||||
*outleafs_count += 1;
|
||||
}
|
||||
|
|
@ -442,6 +377,24 @@ static int OutLeafsToSolid(node_t *node)
|
|||
===========
|
||||
FillOutside
|
||||
|
||||
Goal is to mark brush sides which only touch void as "unbounded" (aka Q2 skip),
|
||||
so they're not used as BSP splitters in the next phase and basically expand outwards.
|
||||
|
||||
Brush sides which cross between void and non-void are problematic for this, so
|
||||
the process looks like:
|
||||
|
||||
1) flood outside -> in from beyond the map bounds and mark these leafs as solid
|
||||
|
||||
Now all leafs marked "empty" are actually empty, not void.
|
||||
|
||||
2) initialize all original brush sides to "invisible"
|
||||
|
||||
2) flood from all empty leafs, mark original brush sides as "visible"
|
||||
|
||||
This will handle partially-void, partially-in-bounds sides (they'll be marked visible).
|
||||
|
||||
fixme-brushbsp: we'll want to do this for detail as well, which means building another set of
|
||||
portals for everything (not just structural).
|
||||
===========
|
||||
*/
|
||||
bool FillOutside(node_t *node, const int hullnum)
|
||||
|
|
@ -508,35 +461,11 @@ bool FillOutside(node_t *node, const int hullnum)
|
|||
return false;
|
||||
}
|
||||
|
||||
// At this point, leafs not reachable from entities have (node->occupied == 0).
|
||||
// The two final tasks are:
|
||||
// 1. Mark the leafs that are not reachable as CONTENTS_SOLID (i.e. filling them in as the void).
|
||||
// 2. Delete faces in those leafs
|
||||
|
||||
// An annoying wrinkle here: there may be leafs with (node->occupied == 0), which means they should be filled in as
|
||||
// void, but they have faces straddling between them and occupied leafs (i.e. leafs which will be CONTENTS_EMPTY
|
||||
// because they're in playable space). See missing_face_simple.map for an example.
|
||||
//
|
||||
// The subtlety is, if we fill these leafs in as solid and delete the inward-facing faces, the only face left
|
||||
// will be the void-and-non-void-straddling face. This face will mess up LinkConvexFaces, since we need to rebuild
|
||||
// the BSP and recalculate the leaf contents, unaware of the fact that we wanted this leaf to be void
|
||||
// (CONTENTS_SOLID), and this face will cause it to be marked as CONTENTS_EMPTY which will manifest as messed up
|
||||
// hull0 collision in game (weapons shoot through the leaf.)
|
||||
//
|
||||
// In order to avoid this scenario, we need to detect those "void-and-non-void-straddling" faces and not fill those
|
||||
// leafs in as solid. This will keep some extra faces around but keep the content types consistent.
|
||||
|
||||
// fixme-brushbsp: the above is no longer relevant
|
||||
|
||||
ResetFacesTouchingOccupiedLeafs(node);
|
||||
MarkFacesTouchingOccupiedLeafs(node);
|
||||
|
||||
/* now go back and fill outside with solid contents */
|
||||
const int outleafs = OutLeafsToSolid(node);
|
||||
|
||||
/* remove faces from filled in leafs */
|
||||
// fixme-brushbsp: don't do this; mark brush sides instead
|
||||
ClearOutFaces(node);
|
||||
// See missing_face_simple.map for a test case with a brush that straddles between void and non-void
|
||||
|
||||
MarkVisibleBrushSides(node);
|
||||
|
||||
logging::print(logging::flag::STAT, " {:8} outleafs\n", outleafs);
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -681,6 +681,10 @@ static void ProcessEntity(mapentity_t *entity, const int hullnum)
|
|||
// assume non-world bmodels are simple
|
||||
PortalizeWorld(entity, nodes, hullnum);
|
||||
|
||||
// flood fills from the void.
|
||||
// marks brush sides which are *only* touching void;
|
||||
// we can skip using them as BSP splitters on the "really good tree"
|
||||
// (effectively expanding those brush sides outwards).
|
||||
if (!options.nofill.value() && FillOutside(nodes, hullnum)) {
|
||||
// fixme-brushbsp: re-add
|
||||
//FreeNodes(nodes);
|
||||
|
|
|
|||
Loading…
Reference in New Issue