Skip to content

Commit

Permalink
Fix _hulls when hull 0 is omitted
Browse files Browse the repository at this point in the history
Add test
  • Loading branch information
ericwa committed Dec 9, 2024
1 parent 04e4095 commit 71cae31
Show file tree
Hide file tree
Showing 3 changed files with 125 additions and 13 deletions.
28 changes: 15 additions & 13 deletions qbsp/qbsp.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1105,18 +1105,24 @@ static void ProcessEntity(mapentity_t &entity, hull_index_t hullnum)
return;
}

// simpler operation for hulls
if (hullnum.value_or(0)) {
if (!ShouldGenerateClipnodes(entity, hullnum)) {
// We still need to emit an empty tree otherwise hull 0 will point past
// the clipnode array (FIXME?).
bspbrush_t::container empty;
tree_t tree;
BrushBSP(tree, entity, empty, tree_split_t::FAST);
// _hulls key
if (!ShouldGenerateClipnodes(entity, hullnum)) {
// We still need to emit an empty tree otherwise hull 0 will point past
// the clipnode array (FIXME?).
bspbrush_t::container empty;
tree_t tree;
BrushBSP(tree, entity, empty, tree_split_t::FAST);
if (hullnum.value_or(0)) {
ExportClipNodes(entity, tree.headnode, hullnum.value());
return;
} else {
MakeTreePortals(tree); // needed to assign leaf bounds
ExportDrawNodes(entity, tree.headnode, map.bsp.dfaces.size());
}
return;
}

// simpler operation for hulls
if (hullnum.value_or(0)) {
tree_t tree;
BrushBSP(tree, entity, brushes, tree_split_t::FAST);
if (map.is_world_entity(entity) && !qbsp_options.nofill.value()) {
Expand Down Expand Up @@ -1145,10 +1151,6 @@ static void ProcessEntity(mapentity_t &entity, hull_index_t hullnum)
return;
}

if (!ShouldGenerateClipnodes(entity, hullnum)) {
return;
}

// full operation for collision (or main hull)
tree_t tree;

Expand Down
77 changes: 77 additions & 0 deletions testmaps/q1_hulls.map
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// Game: Quake
// Format: Valve
// entity 0
{
"mapversion" "220"
"classname" "worldspawn"
"wad" "deprecated/free_wad.wad;deprecated/fence.wad;deprecated/origin.wad;deprecated/hintskip.wad"
"_wateralpha" "0.5"
"_tb_def" "builtin:Quoth2.fgd"
// brush 0
{
( -288 -256 96 ) ( -288 -255 96 ) ( -288 -256 97 ) orangestuff8 [ 0 -1 0 0 ] [ 0 0 -1 16 ] 0 1 1
( -160 -432 96 ) ( -160 -432 97 ) ( -159 -432 96 ) orangestuff8 [ 1 0 0 0 ] [ 0 0 -1 16 ] 0 1 1
( -160 -256 96 ) ( -159 -256 96 ) ( -160 -255 96 ) orangestuff8 [ -1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1
( -80 64 112 ) ( -80 65 112 ) ( -79 64 112 ) orangestuff8 [ 1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1
( -80 576 112 ) ( -79 576 112 ) ( -80 576 113 ) orangestuff8 [ -1 0 0 0 ] [ 0 0 -1 16 ] 0 1 1
( 288 64 112 ) ( 288 64 113 ) ( 288 65 112 ) orangestuff8 [ 0 1 0 0 ] [ 0 0 -1 16 ] 0 1 1
}
// brush 1
{
( -160 0 96 ) ( -160 1 96 ) ( -160 0 97 ) blood3 [ 0 0 -1.0000000000000002 0 ] [ 0 -1.0000000000000002 0 0 ] 0 1 1
( -160 -32 96 ) ( -160 -32 97 ) ( -159 -32 96 ) blood3 [ 1.0000000000000002 0 0 0 ] [ 0 0 1.0000000000000002 32 ] 0 1 1
( -160 0 96 ) ( -159 0 96 ) ( -160 1 96 ) blood3 [ 1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1
( -96 80 112 ) ( -96 81 112 ) ( -95 80 112 ) blood3 [ 1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1
( -96 80 112 ) ( -95 80 112 ) ( -96 80 113 ) blood3 [ 1.0000000000000002 0 0 0 ] [ 0 0 -1.0000000000000002 16 ] 0 1 1
( -144 80 112 ) ( -144 80 113 ) ( -144 81 112 ) blood3 [ 0 0 1.0000000000000002 0 ] [ 0 -1.0000000000000002 0 0 ] 0 1 1
}
}
// entity 1
{
"classname" "info_player_start"
"origin" "-48 -160 136"
"angle" "180"
}
// entity 2
{
"classname" "monster_shambler"
"origin" "-240 -160 136"
}
// entity 3
{
"classname" "func_wall"
"_hulls" "5"
// brush 0
{
( -160 -192 112 ) ( -160 -191 112 ) ( -160 -192 113 ) orangestuff8 [ 0 0 -1.0000000000000002 -48 ] [ 0 -1.0000000000000002 0 0 ] 0 1 1
( -160 -224 112 ) ( -160 -224 113 ) ( -159 -224 112 ) orangestuff8 [ 1.0000000000000002 0 0 0 ] [ 0 0 1.0000000000000002 16 ] 0 1 1
( -160 -192 112 ) ( -159 -192 112 ) ( -160 -191 112 ) orangestuff8 [ 1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1
( -96 -112 208 ) ( -96 -111 208 ) ( -95 -112 208 ) orangestuff8 [ 1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1
( -96 -112 128 ) ( -95 -112 128 ) ( -96 -112 129 ) orangestuff8 [ 1.0000000000000002 0 0 0 ] [ 0 0 -1.0000000000000002 32 ] 0 1 1
( -144 -112 128 ) ( -144 -112 129 ) ( -144 -111 128 ) orangestuff8 [ 0 0 1.0000000000000002 -16 ] [ 0 -1.0000000000000002 0 0 ] 0 1 1
}
}
// entity 4
{
"classname" "info_null"
"origin" "-152 -168 168"
}
// entity 5
{
"classname" "func_wall"
"_hulls" "6"
// brush 0
{
( -160 0 112 ) ( -160 1 112 ) ( -160 0 113 ) blood3 [ 0 0 -1.0000000000000002 -48 ] [ 0 -1.0000000000000002 0 0 ] 0 1 1
( -160 -32 112 ) ( -160 -32 113 ) ( -159 -32 112 ) blood3 [ 1.0000000000000002 0 0 0 ] [ 0 0 1.0000000000000002 16 ] 0 1 1
( -160 0 112 ) ( -159 0 112 ) ( -160 1 112 ) blood3 [ 1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1
( -96 80 208 ) ( -96 81 208 ) ( -95 80 208 ) blood3 [ 1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1
( -96 80 128 ) ( -95 80 128 ) ( -96 80 129 ) blood3 [ 1.0000000000000002 0 0 0 ] [ 0 0 -1.0000000000000002 32 ] 0 1 1
( -144 80 128 ) ( -144 80 129 ) ( -144 81 128 ) blood3 [ 0 0 1.0000000000000002 -16 ] [ 0 -1.0000000000000002 0 0 ] 0 1 1
}
}
// entity 6
{
"classname" "info_null"
"origin" "-152 24 168"
}
33 changes: 33 additions & 0 deletions tests/test_qbsp.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1493,6 +1493,39 @@ TEST(testmapsQ1, sealingHull1Onnode)
EXPECT_EQ(CONTENTS_SOLID, BSP_FindContentsAtPoint(&bsp, 2, &bsp.dmodels[0], player_start_pos + qvec3d(0, 0, 1000)));
}

TEST(testmapsQ1, hullsFlag)
{
const auto [bsp, bspx, prt] = LoadTestmapQ1("q1_hulls.map");

ASSERT_EQ(3, bsp.dmodels.size()); // world and 2 func_wall's

{
const auto in_bmodel_pos = qvec3d(-152, -168, 168);

// the func_wall has _hulls is set to 5 = 0b101, so generate hulls 0 and 2 (blocks shambler and line traces but
// player can walk through)

EXPECT_EQ(CONTENTS_SOLID, BSP_FindContentsAtPoint(&bsp, 0, &bsp.dmodels[1], in_bmodel_pos));
EXPECT_EQ(CONTENTS_EMPTY, BSP_FindContentsAtPoint(&bsp, 1, &bsp.dmodels[1], in_bmodel_pos));
EXPECT_EQ(CONTENTS_SOLID, BSP_FindContentsAtPoint(&bsp, 2, &bsp.dmodels[1], in_bmodel_pos));

EXPECT_TRUE(BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[1], in_bmodel_pos + qvec3d(8, 0, 0)));
}

{
// the second one has _hulls 6 = 0b110, so generate hulls 1 and 2 (blocks player + shambler, but no visual
// faces and point-size hull traces can pass through)

const auto in_bmodel_pos2 = qvec3d(-152, 24, 168);

EXPECT_EQ(CONTENTS_EMPTY, BSP_FindContentsAtPoint(&bsp, 0, &bsp.dmodels[2], in_bmodel_pos2));
EXPECT_EQ(CONTENTS_SOLID, BSP_FindContentsAtPoint(&bsp, 1, &bsp.dmodels[2], in_bmodel_pos2));
EXPECT_EQ(CONTENTS_SOLID, BSP_FindContentsAtPoint(&bsp, 2, &bsp.dmodels[2], in_bmodel_pos2));

EXPECT_FALSE(BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[2], in_bmodel_pos2 + qvec3d(8, 0, 0)));
}
}

TEST(testmapsQ1, 0125UnitFaces)
{
GTEST_SKIP();
Expand Down

0 comments on commit 71cae31

Please sign in to comment.