qbsp: sealing: in hull1/2 treat onnode point entities as being in empty space

This commit is contained in:
Eric Wasylishen 2023-04-07 17:06:56 -06:00
parent 00bcead0fb
commit 8d368060eb
3 changed files with 101 additions and 11 deletions

View File

@ -48,13 +48,18 @@ static bool LeafSealsMap(const node_t *node)
PointInLeaf PointInLeaf
If the point is exactly on a node plane, prefer to return the If the point is exactly on a node plane, prefer to return the
opaque leaf. one that seals the map if `prefer_sealing` is true (otherwise
prefer the one that doesn't seal).
This avoids spurious leaks if a point entity is on the outside This avoids spurious leaks if a point entity is on the outside
of the map (exactly on a brush faces) - happens in base1.map. of the map (exactly on a brush faces) - happens in base1.map.
However, in Q1 hull1/hull2, it makes more sense to prefer the empty
leaf, so an info_player_start 24 units off a floor causes the
room to not get filled in as solid.
=========== ===========
*/ */
static node_t *PointInLeaf(node_t *node, const qvec3d &point) static node_t *PointInLeaf(node_t *node, const qvec3d &point, bool prefer_sealing)
{ {
if (node->is_leaf) { if (node->is_leaf) {
return node; return node;
@ -64,18 +69,17 @@ static node_t *PointInLeaf(node_t *node, const qvec3d &point)
if (dist > 0) { if (dist > 0) {
// point is on the front of the node plane // point is on the front of the node plane
return PointInLeaf(node->children[0], point); return PointInLeaf(node->children[0], point, prefer_sealing);
} else if (dist < 0) { } else if (dist < 0) {
// point is on the back of the node plane // point is on the back of the node plane
return PointInLeaf(node->children[1], point); return PointInLeaf(node->children[1], point, prefer_sealing);
} else { } else {
// point is exactly on the node plane // point is exactly on the node plane
node_t *front = PointInLeaf(node->children[0], point); node_t *front = PointInLeaf(node->children[0], point, prefer_sealing);
node_t *back = PointInLeaf(node->children[1], point); node_t *back = PointInLeaf(node->children[1], point, prefer_sealing);
// prefer the opaque one if (prefer_sealing == LeafSealsMap(front)) {
if (LeafSealsMap(front)) {
return front; return front;
} }
return back; return back;
@ -324,7 +328,7 @@ FindOccupiedLeafs
sets node->occupant sets node->occupant
================== ==================
*/ */
static void MarkOccupiedClusters(node_t *headnode) static void MarkOccupiedClusters(node_t *headnode, hull_index_t hullnum)
{ {
for (int i = 1; i < map.entities.size(); i++) { for (int i = 1; i < map.entities.size(); i++) {
mapentity_t &entity = map.entities.at(i); mapentity_t &entity = map.entities.at(i);
@ -339,7 +343,8 @@ static void MarkOccupiedClusters(node_t *headnode)
} }
/* find the leaf it's in. Skip opqaue leafs */ /* find the leaf it's in. Skip opqaue leafs */
node_t *cluster = PointInLeaf(headnode, entity.origin); bool prefer_sealing = !hullnum.has_value() || hullnum.value() == 0;
node_t *cluster = PointInLeaf(headnode, entity.origin, prefer_sealing);
if (LeafSealsMap(cluster)) { if (LeafSealsMap(cluster)) {
continue; continue;
@ -630,7 +635,7 @@ bool FillOutside(tree_t &tree, hull_index_t hullnum, bspbrush_t::container &brus
ClearOccupied_r(node); ClearOccupied_r(node);
// Sets leaf->occupant // Sets leaf->occupant
MarkOccupiedClusters(node); MarkOccupiedClusters(node, hullnum);
const std::vector<node_t *> occupied_clusters = FindOccupiedClusters(node); const std::vector<node_t *> occupied_clusters = FindOccupiedClusters(node);
for (auto *occupied_cluster : occupied_clusters) { for (auto *occupied_cluster : occupied_clusters) {

View File

@ -0,0 +1,66 @@
// Game: Quake
// Format: Standard
// entity 0
{
"classname" "worldspawn"
"wad" "deprecated/free_wad.wad"
// brush 0
{
( -304 32 16 ) ( -304 256 16 ) ( -304 32 192 ) bolt9 0 0 0 1 1
( -304 32 192 ) ( -288 32 192 ) ( -304 32 16 ) bolt9 0 0 0 1 1
( -304 32 16 ) ( -288 32 16 ) ( -304 256 16 ) bolt9 0 0 0 1 1
( -304 256 192 ) ( -288 256 192 ) ( -304 32 192 ) bolt9 0 0 0 1 1
( -304 256 16 ) ( -288 256 16 ) ( -304 256 192 ) bolt9 0 0 0 1 1
( -288 32 16 ) ( -288 32 192 ) ( -288 256 16 ) bolt9 0 0 0 1 1
}
// brush 1
{
( -288 256 192 ) ( -288 240 192 ) ( -288 256 16 ) bolt9 0 0 0 1 1
( -64 240 192 ) ( -64 240 16 ) ( -288 240 192 ) bolt9 0 0 0 1 1
( -288 256 16 ) ( -288 240 16 ) ( -64 256 16 ) bolt9 0 0 0 1 1
( -64 256 192 ) ( -64 240 192 ) ( -288 256 192 ) bolt9 0 0 0 1 1
( -64 256 192 ) ( -288 256 192 ) ( -64 256 16 ) bolt9 0 0 0 1 1
( 224 256 16 ) ( 224 240 16 ) ( 224 256 192 ) bolt9 0 0 0 1 1
}
// brush 2
{
( -288 32 16 ) ( -288 48 16 ) ( -288 32 192 ) bolt9 0 0 0 1 1
( -64 32 16 ) ( -288 32 16 ) ( -64 32 192 ) bolt9 0 0 0 1 1
( -64 32 16 ) ( -64 48 16 ) ( -288 32 16 ) bolt9 0 0 0 1 1
( -288 32 192 ) ( -288 48 192 ) ( -64 32 192 ) bolt9 0 0 0 1 1
( -288 48 16 ) ( -64 48 16 ) ( -288 48 192 ) bolt9 0 0 0 1 1
( 224 32 192 ) ( 224 48 192 ) ( 224 32 16 ) bolt9 0 0 0 1 1
}
// brush 3
{
( -288 48 192 ) ( -288 48 176 ) ( -288 240 192 ) bolt9 0 0 0 1 1
( -64 48 192 ) ( -64 48 176 ) ( -288 48 192 ) bolt9 0 0 0 1 1
( -64 240 176 ) ( -288 240 176 ) ( -64 48 176 ) bolt9 0 0 0 1 1
( -64 240 192 ) ( -64 48 192 ) ( -288 240 192 ) bolt9 0 0 0 1 1
( -288 240 192 ) ( -288 240 176 ) ( -64 240 192 ) bolt9 0 0 0 1 1
( 224 240 192 ) ( 224 240 176 ) ( 224 48 192 ) bolt9 0 0 0 1 1
}
// brush 4
{
( -288 240 16 ) ( -288 240 32 ) ( -288 48 16 ) bolt9 0 0 0 1 1
( -288 48 16 ) ( -288 48 32 ) ( -64 48 16 ) bolt9 0 0 0 1 1
( -288 240 16 ) ( -288 48 16 ) ( -64 240 16 ) bolt9 0 0 0 1 1
( -288 48 32 ) ( -288 240 32 ) ( -64 48 32 ) bolt9 0 0 0 1 1
( -64 240 16 ) ( -64 240 32 ) ( -288 240 16 ) bolt9 0 0 0 1 1
( 224 48 16 ) ( 224 48 32 ) ( 224 240 16 ) bolt9 0 0 0 1 1
}
// brush 5
{
( 208 48 32 ) ( 208 49 32 ) ( 208 48 33 ) bolt9 0 0 0 1 1
( 208 48 32 ) ( 208 48 33 ) ( 209 48 32 ) bolt9 0 0 0 1 1
( 208 48 32 ) ( 209 48 32 ) ( 208 49 32 ) bolt9 0 0 0 1 1
( 224 240 192 ) ( 224 241 192 ) ( 225 240 192 ) bolt9 0 0 0 1 1
( 224 240 40 ) ( 225 240 40 ) ( 224 240 41 ) bolt9 0 0 0 1 1
( 224 240 40 ) ( 224 240 41 ) ( 224 241 40 ) bolt9 0 0 0 1 1
}
}
// entity 1
{
"classname" "info_player_start"
"origin" "-192 132 56"
}

View File

@ -1211,6 +1211,25 @@ TEST_CASE("qbsp_sealing_point_entity_on_outside" * doctest::test_suite("testmaps
REQUIRE(prt.has_value()); REQUIRE(prt.has_value());
} }
TEST_CASE("q1_sealing_hull1_onnode" * doctest::test_suite("testmaps_q1"))
{
const auto [bsp, bspx, prt] = LoadTestmapQ1("q1_sealing_hull1_onnode.map");
const auto player_start_pos = qvec3d(-192, 132, 56);
INFO("hull0 is empty at the player start");
CHECK(CONTENTS_EMPTY == BSP_FindContentsAtPoint(&bsp, 0, &bsp.dmodels[0], player_start_pos));
INFO("hull1/2 are empty just above the player start");
CHECK(CONTENTS_EMPTY == BSP_FindContentsAtPoint(&bsp, 1, &bsp.dmodels[0], player_start_pos + qvec3d(0, 0, 1)));
CHECK(CONTENTS_EMPTY == BSP_FindContentsAtPoint(&bsp, 2, &bsp.dmodels[0], player_start_pos + qvec3d(0, 0, 1)));
INFO("hull0/1/2 are solid in the void");
CHECK(CONTENTS_SOLID == BSP_FindContentsAtPoint(&bsp, 0, &bsp.dmodels[0], player_start_pos + qvec3d(0, 0, 1000)));
CHECK(CONTENTS_SOLID == BSP_FindContentsAtPoint(&bsp, 1, &bsp.dmodels[0], player_start_pos + qvec3d(0, 0, 1000)));
CHECK(CONTENTS_SOLID == BSP_FindContentsAtPoint(&bsp, 2, &bsp.dmodels[0], player_start_pos + qvec3d(0, 0, 1000)));
}
TEST_CASE("q1_0125unit_faces" * doctest::test_suite("testmaps_q1") * doctest::may_fail()) TEST_CASE("q1_0125unit_faces" * doctest::test_suite("testmaps_q1") * doctest::may_fail())
{ {
const auto [bsp, bspx, prt] = LoadTestmapQ1("q1_0125unit_faces.map"); const auto [bsp, bspx, prt] = LoadTestmapQ1("q1_0125unit_faces.map");