qbsp: sync FindAreas to Quake-2-Tools version

fixes area portals in cases when area has no point entities in it
This commit is contained in:
Eric Wasylishen 2022-09-10 15:49:17 -06:00
parent a8a8966e28
commit 68e2f97fbf
4 changed files with 254 additions and 36 deletions

View File

@ -641,32 +641,6 @@ static void FloodAreas_r(node_t *node)
}
}
static void FloodNode(node_t *node)
{
if (node->area)
return;
// area portals are always only flooded into, never
// out of
if (ClusterContents(node).native & Q2_CONTENTS_AREAPORTAL)
return;
map.c_areas++;
FloodAreas_r(node);
}
static void FloodNodes_R(node_t *node)
{
if (!node) {
return;
}
FloodNode(node);
FloodNodes_R(node->children[0]);
FloodNodes_R(node->children[1]);
}
/*
=============
FindAreas_r
@ -675,19 +649,30 @@ Just decend the tree, and for each node that hasn't had an
area set, flood fill out from there
=============
*/
static void FindAreas(node_t *node)
static void FindAreas_r(node_t *node)
{
auto leaves = FindOccupiedClusters(node);
if (leaves.empty()) {
// map leaked, just flood entire map
FloodNodes_R(node);
if (!node->is_leaf) {
FindAreas_r(node->children[0]);
FindAreas_r(node->children[1]);
return;
}
if (node->area)
return; // already got it
for (auto *leaf : leaves) {
FloodNode(leaf);
}
if (node->contents.is_any_solid(qbsp_options.target_game))
return;
if (!node->occupied)
return; // not reachable from an entity
// area portals are always only flooded into, never
// out of
if (node->contents.native & Q2_CONTENTS_AREAPORTAL)
return;
map.c_areas++;
FloodAreas_r(node);
}
/*
@ -740,7 +725,7 @@ void EmitAreaPortals(node_t *headnode)
{
logging::funcheader();
FindAreas(headnode);
FindAreas_r(headnode);
SetAreaPortalAreas_r(headnode);
map.bsp.dareaportals.emplace_back();

View File

@ -0,0 +1,88 @@
// Game: Quake 2
// Format: Quake2
// entity 0
{
"classname" "worldspawn"
"_tb_textures" "textures/e1u1"
// brush 0
{
( 48 64 112 ) ( 48 -64 112 ) ( 48 64 -16 ) e1u1/skip 0 0 0 1 1 0 128 0
( 64 -64 -16 ) ( 48 -64 -16 ) ( 64 -64 112 ) e1u1/skip 0 0 0 1 1 0 128 0
( 64 64 -16 ) ( 48 64 -16 ) ( 64 -64 -16 ) e1u1/skip 0 0 0 1 1 0 128 0
( 64 -64 112 ) ( 48 -64 112 ) ( 64 64 112 ) e1u1/skip 0 0 0 1 1 0 128 0
( 64 64 112 ) ( 48 64 112 ) ( 64 64 -16 ) e1u1/skip 0 0 0 1 1 0 128 0
( 64 -64 112 ) ( 64 64 112 ) ( 64 -64 -16 ) e1u1/skip 0 0 0 1 1 0 128 0
}
// brush 1
{
( -64 -64 -16 ) ( -64 64 -16 ) ( -64 -64 112 ) e1u1/skip 0 0 0 1 1 0 128 0
( -64 -64 112 ) ( -48 -64 112 ) ( -64 -64 -16 ) e1u1/skip 0 0 0 1 1 0 128 0
( -64 -64 -16 ) ( -48 -64 -16 ) ( -64 64 -16 ) e1u1/skip 0 0 0 1 1 0 128 0
( -64 64 112 ) ( -48 64 112 ) ( -64 -64 112 ) e1u1/skip 0 0 0 1 1 0 128 0
( -64 64 -16 ) ( -48 64 -16 ) ( -64 64 112 ) e1u1/skip 0 0 0 1 1 0 128 0
( -48 -64 -16 ) ( -48 -64 112 ) ( -48 64 -16 ) e1u1/skip 0 0 0 1 1 0 128 0
}
// brush 2
{
( -48 64 112 ) ( -48 48 112 ) ( -48 64 -16 ) e1u1/skip 0 0 0 1 1 0 128 0
( 48 48 112 ) ( 48 48 -16 ) ( -48 48 112 ) e1u1/skip 0 0 0 1 1 0 128 0
( -48 64 -16 ) ( -48 48 -16 ) ( 48 64 -16 ) e1u1/skip 0 0 0 1 1 0 128 0
( 48 64 112 ) ( 48 48 112 ) ( -48 64 112 ) e1u1/skip 0 0 0 1 1 0 128 0
( 48 64 112 ) ( -48 64 112 ) ( 48 64 -16 ) e1u1/skip 0 0 0 1 1 0 128 0
( 48 64 -16 ) ( 48 48 -16 ) ( 48 64 112 ) e1u1/skip 0 0 0 1 1 0 128 0
}
// brush 3
{
( -48 -64 -16 ) ( -48 -48 -16 ) ( -48 -64 112 ) e1u1/skip 0 0 0 1 1 0 128 0
( 48 -64 -16 ) ( -48 -64 -16 ) ( 48 -64 112 ) e1u1/skip 0 0 0 1 1 0 128 0
( 48 -64 -16 ) ( 48 -48 -16 ) ( -48 -64 -16 ) e1u1/skip 0 0 0 1 1 0 128 0
( -48 -64 112 ) ( -48 -48 112 ) ( 48 -64 112 ) e1u1/skip 0 0 0 1 1 0 128 0
( -48 -48 -16 ) ( 48 -48 -16 ) ( -48 -48 112 ) e1u1/skip 0 0 0 1 1 0 128 0
( 48 -64 112 ) ( 48 -48 112 ) ( 48 -64 -16 ) e1u1/skip 0 0 0 1 1 0 128 0
}
// brush 4
{
( -48 -48 112 ) ( -48 -48 96 ) ( -48 48 112 ) e1u1/skip 0 0 0 1 1 0 128 0
( 48 -48 112 ) ( 48 -48 96 ) ( -48 -48 112 ) e1u1/skip 0 0 0 1 1 0 128 0
( 48 48 96 ) ( -48 48 96 ) ( 48 -48 96 ) e1u1/skip 0 0 0 1 1 0 128 0
( 48 48 112 ) ( 48 -48 112 ) ( -48 48 112 ) e1u1/skip 0 0 0 1 1 0 128 0
( -48 48 112 ) ( -48 48 96 ) ( 48 48 112 ) e1u1/skip 0 0 0 1 1 0 128 0
( 48 48 112 ) ( 48 48 96 ) ( 48 -48 112 ) e1u1/skip 0 0 0 1 1 0 128 0
}
// brush 5
{
( -48 48 -16 ) ( -48 48 0 ) ( -48 -48 -16 ) e1u1/skip 0 0 0 1 1 0 128 0
( -48 -48 -16 ) ( -48 -48 0 ) ( 48 -48 -16 ) e1u1/skip 0 0 0 1 1 0 128 0
( -48 48 -16 ) ( -48 -48 -16 ) ( 48 48 -16 ) e1u1/skip 0 0 0 1 1 0 128 0
( -48 -48 0 ) ( -48 48 0 ) ( 48 -48 0 ) e1u1/skip 0 0 0 1 1 0 128 0
( 48 48 -16 ) ( 48 48 0 ) ( -48 48 -16 ) e1u1/skip 0 0 0 1 1 0 128 0
( 48 -48 -16 ) ( 48 -48 0 ) ( 48 48 -16 ) e1u1/skip 0 0 0 1 1 0 128 0
}
// brush 6
{
( -48 0 80 ) ( -48 1 80 ) ( -48 0 81 ) e1u1/skip 0 16 0 1 1
( -48 -48 80 ) ( -48 -48 81 ) ( -47 -48 80 ) e1u1/skip 0 16 0 1 1
( -48 0 80 ) ( -47 0 80 ) ( -48 1 80 ) e1u1/box3_4 0 0 0 1 1
( 48 16 96 ) ( 48 17 96 ) ( 49 16 96 ) e1u1/skip 0 0 0 1 1
( 48 48 96 ) ( 49 48 96 ) ( 48 48 97 ) e1u1/skip 0 16 0 1 1
( 48 16 96 ) ( 48 16 97 ) ( 48 17 96 ) e1u1/skip 0 16 0 1 1
}
}
// entity 1
{
"classname" "func_areaportal"
// brush 0
{
( -48 0 0 ) ( -48 1 0 ) ( -48 0 1 ) e1u1/trigger 0 0 0 1 1
( -16 0 0 ) ( -16 0 1 ) ( -15 0 0 ) e1u1/trigger 0 0 0 1 1
( -16 0 0 ) ( -15 0 0 ) ( -16 1 0 ) e1u1/trigger 0 0 0 1 1
( 48 16 80 ) ( 48 17 80 ) ( 49 16 80 ) e1u1/trigger 0 0 0 1 1
( 48 16 16 ) ( 49 16 16 ) ( 48 16 17 ) e1u1/trigger 0 0 0 1 1
( 48 16 16 ) ( 48 16 17 ) ( 48 17 16 ) e1u1/trigger 0 0 0 1 1
}
}
// entity 2
{
"classname" "info_player_start"
"origin" "16 -32 24"
}

View File

@ -0,0 +1,117 @@
// Game: Quake 2
// Format: Quake2
// entity 0
{
"classname" "worldspawn"
"_tb_textures" "textures/e1u1"
// brush 0
{
( 60 128 112 ) ( 60 -128 112 ) ( 60 128 -16 ) e1u1/c_met11_2 0 0 0 1 1
( 64 -128 -16 ) ( 60 -128 -16 ) ( 64 -128 112 ) e1u1/c_met11_2 0 0 0 1 1
( 64 128 -16 ) ( 60 128 -16 ) ( 64 -128 -16 ) e1u1/c_met11_2 0 0 0 1 1
( 64 -128 112 ) ( 60 -128 112 ) ( 64 128 112 ) e1u1/c_met11_2 0 0 0 1 1
( 64 128 112 ) ( 60 128 112 ) ( 64 128 -16 ) e1u1/c_met11_2 0 0 0 1 1
( 64 -128 112 ) ( 64 128 112 ) ( 64 -128 -16 ) e1u1/c_met11_2 0 0 0 1 1
}
// brush 1
{
( -64 -128 -16 ) ( -64 128 -16 ) ( -64 -128 112 ) e1u1/c_met11_2 0 0 0 1 1
( -64 -128 112 ) ( -60 -128 112 ) ( -64 -128 -16 ) e1u1/c_met11_2 0 0 0 1 1
( -64 -128 -16 ) ( -60 -128 -16 ) ( -64 128 -16 ) e1u1/c_met11_2 0 0 0 1 1
( -64 128 112 ) ( -60 128 112 ) ( -64 -128 112 ) e1u1/c_met11_2 0 0 0 1 1
( -64 128 -16 ) ( -60 128 -16 ) ( -64 128 112 ) e1u1/c_met11_2 0 0 0 1 1
( -60 -128 -16 ) ( -60 -128 112 ) ( -60 128 -16 ) e1u1/c_met11_2 0 0 0 1 1
}
// brush 2
{
( -60 128 112 ) ( -60 124 112 ) ( -60 128 -16 ) e1u1/c_met11_2 0 0 0 1 1
( 60 124 112 ) ( 60 124 -16 ) ( -60 124 112 ) e1u1/c_met11_2 0 0 0 1 1
( -60 128 -16 ) ( -60 124 -16 ) ( 60 128 -16 ) e1u1/c_met11_2 0 0 0 1 1
( 60 128 112 ) ( 60 124 112 ) ( -60 128 112 ) e1u1/c_met11_2 0 0 0 1 1
( 60 128 112 ) ( -60 128 112 ) ( 60 128 -16 ) e1u1/c_met11_2 0 0 0 1 1
( 60 128 -16 ) ( 60 124 -16 ) ( 60 128 112 ) e1u1/c_met11_2 0 0 0 1 1
}
// brush 3
{
( -60 -128 -16 ) ( -60 -124 -16 ) ( -60 -128 112 ) e1u1/c_met11_2 0 0 0 1 1
( 60 -128 -16 ) ( -60 -128 -16 ) ( 60 -128 112 ) e1u1/c_met11_2 0 0 0 1 1
( 60 -128 -16 ) ( 60 -124 -16 ) ( -60 -128 -16 ) e1u1/c_met11_2 0 0 0 1 1
( -60 -128 112 ) ( -60 -124 112 ) ( 60 -128 112 ) e1u1/c_met11_2 0 0 0 1 1
( -60 -124 -16 ) ( 60 -124 -16 ) ( -60 -124 112 ) e1u1/c_met11_2 0 0 0 1 1
( 60 -128 112 ) ( 60 -124 112 ) ( 60 -128 -16 ) e1u1/c_met11_2 0 0 0 1 1
}
// brush 4
{
( -60 -124 112 ) ( -60 -124 108 ) ( -60 124 112 ) e1u1/c_met11_2 0 0 0 1 1
( 60 -124 112 ) ( 60 -124 108 ) ( -60 -124 112 ) e1u1/c_met11_2 0 0 0 1 1
( 60 124 108 ) ( -60 124 108 ) ( 60 -124 108 ) e1u1/c_met11_2 0 0 0 1 1
( 60 124 112 ) ( 60 -124 112 ) ( -60 124 112 ) e1u1/c_met11_2 0 0 0 1 1
( -60 124 112 ) ( -60 124 108 ) ( 60 124 112 ) e1u1/c_met11_2 0 0 0 1 1
( 60 124 112 ) ( 60 124 108 ) ( 60 -124 112 ) e1u1/c_met11_2 0 0 0 1 1
}
// brush 5
{
( -60 124 -16 ) ( -60 124 -12 ) ( -60 -124 -16 ) e1u1/c_met11_2 0 0 0 1 1
( -60 -124 -16 ) ( -60 -124 -12 ) ( 60 -124 -16 ) e1u1/c_met11_2 0 0 0 1 1
( -60 124 -16 ) ( -60 -124 -16 ) ( 60 124 -16 ) e1u1/c_met11_2 0 0 0 1 1
( -60 -124 -12 ) ( -60 124 -12 ) ( 60 -124 -12 ) e1u1/c_met11_2 0 0 0 1 1
( 60 124 -16 ) ( 60 124 -12 ) ( -60 124 -16 ) e1u1/c_met11_2 0 0 0 1 1
( 60 -124 -16 ) ( 60 -124 -12 ) ( 60 124 -16 ) e1u1/c_met11_2 0 0 0 1 1
}
// brush 6
{
( 0 48 -12 ) ( 0 49 -12 ) ( 0 48 -11 ) e1u1/c_met11_2 0 0 0 1 1
( 52 32 -12 ) ( 52 32 -11 ) ( 53 32 -12 ) e1u1/c_met11_2 0 0 0 1 1
( 52 48 -12 ) ( 53 48 -12 ) ( 52 49 -12 ) e1u1/c_met11_2 0 0 0 1 1
( 60 56 108 ) ( 60 57 108 ) ( 61 56 108 ) e1u1/c_met11_2 0 0 0 1 1
( 60 56 -8 ) ( 61 56 -8 ) ( 60 56 -7 ) e1u1/c_met11_2 0 0 0 1 1
( 60 56 -8 ) ( 60 56 -7 ) ( 60 57 -8 ) e1u1/c_met11_2 0 0 0 1 1
}
}
// entity 1
{
"classname" "info_player_start"
"origin" "-4 -88 12"
}
// entity 2
{
"classname" "func_areaportal"
"targetname" "a1"
// brush 0
{
( -60 48 -12 ) ( -60 49 -12 ) ( -60 48 -11 ) e1u1/trigger 4 0 0 1 1
( -60 48 -12 ) ( -60 48 -11 ) ( -59 48 -12 ) e1u1/trigger 0 0 0 1 1
( -60 48 -12 ) ( -59 48 -12 ) ( -60 49 -12 ) e1u1/trigger 0 -4 0 1 1
( 0 52 108 ) ( 0 53 108 ) ( 1 52 108 ) e1u1/trigger 0 -4 0 1 1
( 0 52 -8 ) ( 1 52 -8 ) ( 0 52 -7 ) e1u1/trigger 0 0 0 1 1
( 0 52 -8 ) ( 0 52 -7 ) ( 0 53 -8 ) e1u1/trigger 4 0 0 1 1
}
}
// entity 3
{
"classname" "func_areaportal"
"targetname" "a1"
// brush 0
{
( -60 36 -12 ) ( -60 37 -12 ) ( -60 36 -11 ) e1u1/trigger 16 0 0 1 1
( -60 36 -12 ) ( -60 36 -11 ) ( -59 36 -12 ) e1u1/trigger 0 0 0 1 1
( -60 36 -12 ) ( -59 36 -12 ) ( -60 37 -12 ) e1u1/trigger 0 -16 0 1 1
( 0 40 108 ) ( 0 41 108 ) ( 1 40 108 ) e1u1/trigger 0 -16 0 1 1
( 0 40 -8 ) ( 1 40 -8 ) ( 0 40 -7 ) e1u1/trigger 0 0 0 1 1
( 0 40 -8 ) ( 0 40 -7 ) ( 0 41 -8 ) e1u1/trigger 16 0 0 1 1
}
}
// entity 4
{
"classname" "func_door"
"target" "a1"
// brush 0
{
( -60 32 -12 ) ( -60 33 -12 ) ( -60 32 -11 ) e1u1/grndoor1 0 0 0 1 1
( -60 32 -12 ) ( -60 32 -11 ) ( -59 32 -12 ) e1u1/grndoor1 0 0 0 1 1
( -60 32 -12 ) ( -59 32 -12 ) ( -60 33 -12 ) e1u1/grndoor1 0 0 0 1 1
( 0 48 108 ) ( 0 49 108 ) ( 1 48 108 ) e1u1/grndoor1 0 0 0 1 1
( 0 56 -8 ) ( 1 56 -8 ) ( 0 56 -7 ) e1u1/grndoor1 0 0 0 1 1
( 0 48 -8 ) ( 0 48 -7 ) ( 0 49 -8 ) e1u1/grndoor1 0 0 0 1 1
}
}

View File

@ -1740,6 +1740,34 @@ TEST_CASE("qbsp_q2_detail_seals", "[testmaps_q2]") {
CHECK(Q2_CONTENTS_SOLID == BSP_FindLeafAtPoint(&bsp, &bsp.dmodels[0], in_void)->contents);
}
/**
* Two areaportals with a small gap in between creating another area.
*
* Also, the faces on the ceiling/floor cross the areaportal
* (due to our aggressive face merging).
*/
TEST_CASE("q2_double_areaportal", "[testmaps_q2]")
{
const auto [bsp, bspx, prt] = LoadTestmapQ2("q2_double_areaportal.map");
CHECK(GAME_QUAKE_II == bsp.loadversion->game->id);
CheckFilled(bsp);
CHECK(4 == bsp.dareas.size());
CHECK(5 == bsp.dareaportals.size());
}
TEST_CASE("q2_areaportal_split", "[testmaps_q2]")
{
const auto [bsp, bspx, prt] = LoadTestmapQ2("q2_areaportal_split.map");
CHECK(GAME_QUAKE_II == bsp.loadversion->game->id);
CheckFilled(bsp);
CHECK(3 == bsp.dareas.size()); // 1 invalid index zero reserved + 2 areas
CHECK(2 == bsp.dareaportals.size()); // 1 invalid index zero reserved + 1 portal
}
/**
* Q1 sealing test:
* - hull0 can use Q2 method (fill inside)