From 39d19304463f689aaba80b8af606a3b157ec4e69 Mon Sep 17 00:00:00 2001 From: Eric Wasylishen Date: Sat, 9 Apr 2022 21:46:01 -0600 Subject: [PATCH] outside.cc: prototype marking visible brush sides --- include/qbsp/outside.hh | 2 +- qbsp/outside.cc | 71 +++++++++++++++++++++++++++++++++++++---- qbsp/qbsp.cc | 4 +-- 3 files changed, 67 insertions(+), 10 deletions(-) diff --git a/include/qbsp/outside.hh b/include/qbsp/outside.hh index c6f86d3b..f8879172 100644 --- a/include/qbsp/outside.hh +++ b/include/qbsp/outside.hh @@ -21,5 +21,5 @@ #pragma once -bool FillOutside(node_t *node, const int hullnum); +bool FillOutside(mapentity_t *entity, node_t *node, const int hullnum); std::vector FindOccupiedClusters(node_t *headnode); \ No newline at end of file diff --git a/qbsp/outside.cc b/qbsp/outside.cc index 2dbb5054..4f2941c2 100644 --- a/qbsp/outside.cc +++ b/qbsp/outside.cc @@ -321,6 +321,23 @@ std::vector FindOccupiedClusters(node_t *headnode) return result; } +//============================================================================= + +static void AssertBrushSidesInvisible(node_t *node) +{ + if (node->planenum != PLANENUM_LEAF) { + AssertBrushSidesInvisible(node->children[0]); + AssertBrushSidesInvisible(node->children[1]); + return; + } + + for (auto *brush : node->original_brushes) { + for (const auto &face : brush->faces) { + Q_assert(!face.visible); + } + } +} + /* ================== MarkVisibleBrushSides @@ -336,13 +353,32 @@ static void MarkVisibleBrushSides(node_t *node) return; } - // visit the leaf + if (node->opaque()) { + return; + } - // 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'. + // visit the non-opaque leaf: check all portals to neighbouring leafs. + + int side; + for (portal_t *portal = node->portals; portal; portal = portal->next[!side]) { + side = (portal->nodes[0] == node); + + node_t *neighbour_leaf = portal->nodes[side]; + + for (auto *brush : neighbour_leaf->original_brushes) { + for (auto &side : brush->faces) { + if (side.planenum == portal->planenum) { + // we've found a brush side in an original brush in the neighbouring + // leaf, on a portal to this (non-opaque) leaf, so mark it as visible. + side.visible = true; + } + } + } + } } +//============================================================================= + static void OutLeafsToSolid_r(node_t *node, int *outleafs_count) { if (node->planenum != PLANENUM_LEAF) { @@ -393,11 +429,15 @@ Now all leafs marked "empty" are actually empty, not void. This will handle partially-void, partially-in-bounds sides (they'll be marked visible). +(doing it the opposite way, defaulting brushes to "visible" and flood-filling +from the void wouldn't work, because brush sides that cross into the map would +get incorrectly marked as "invisible"). + 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) +bool FillOutside(mapentity_t *entity, node_t *node, const int hullnum) { logging::print(logging::flag::PROGRESS, "---- {} ----\n", __func__); @@ -461,12 +501,29 @@ bool FillOutside(node_t *node, const int hullnum) return false; } + // change the leaf contents const int outleafs = OutLeafsToSolid(node); // See missing_face_simple.map for a test case with a brush that straddles between void and non-void - - MarkVisibleBrushSides(node); + AssertBrushSidesInvisible(node); + + MarkVisibleBrushSides(node); + + // Count brush sides + int visible_brush_sides = 0; + int invisible_brush_sides = 0; + for (const auto &brush : entity->brushes) { + for (auto &side : brush.faces) { + if (side.visible) { + ++visible_brush_sides; + } else { + ++invisible_brush_sides; + } + } + } logging::print(logging::flag::STAT, " {:8} outleafs\n", outleafs); + logging::print(logging::flag::STAT, " {:8} visible brush sides\n", visible_brush_sides); + logging::print(logging::flag::STAT, " {:8} invisible brush sides\n", invisible_brush_sides); return true; } diff --git a/qbsp/qbsp.cc b/qbsp/qbsp.cc index d93267d3..a1176646 100644 --- a/qbsp/qbsp.cc +++ b/qbsp/qbsp.cc @@ -645,7 +645,7 @@ static void ProcessEntity(mapentity_t *entity, const int hullnum) if (entity == pWorldEnt() && !options.nofill.value()) { // assume non-world bmodels are simple PortalizeWorld(entity, nodes, hullnum); - if (FillOutside(nodes, hullnum)) { + if (FillOutside(entity, nodes, hullnum)) { // fixme-brushbsp: re-add // FreeNodes(nodes); @@ -685,7 +685,7 @@ static void ProcessEntity(mapentity_t *entity, const int hullnum) // 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)) { + if (!options.nofill.value() && FillOutside(entity, nodes, hullnum)) { // fixme-brushbsp: re-add //FreeNodes(nodes);