Add _hulls property to allow a bmodel to set for which hulls it should have clipnodes (#442)
* Add _hulls bmodel property Whitelist hulls for which to generate clipnodes. * Fix _hulls when hull 0 is omitted Add test --------- Co-authored-by: Eric Wasylishen <ewasylishen@gmail.com>
This commit is contained in:
parent
0f1a7186d6
commit
d0788ac01f
|
|
@ -811,6 +811,17 @@ Model Entity Keys
|
|||
Defaults to 0, brushes with higher values (equivalent to appearing later in the .map file) will clip away lower
|
||||
valued brushes.
|
||||
|
||||
.. bmodel-key:: "_hulls" "n"
|
||||
|
||||
Bitmap ("Flags" type in FGD) that selects for which hulls collision data
|
||||
will be generated. eg. a decimal value of 11 (0b1011) would generate hull 0, hull 1,
|
||||
and hull 3.
|
||||
Faces are computed using data from hull 0, not generating this hull will
|
||||
prevent a bmodel from being rendered, acting as a CLIP brush only active for
|
||||
the specified hulls.
|
||||
|
||||
Defaults to 0 which will generate clipnodes for all hulls.
|
||||
|
||||
.. bmodel-key:: "_chop" "n"
|
||||
|
||||
Set to 0 to prevent these brushes from being chopped.
|
||||
|
|
|
|||
34
qbsp/qbsp.cc
34
qbsp/qbsp.cc
|
|
@ -984,6 +984,24 @@ static void GatherLeafVolumes_r(node_t *node, bspbrush_t::container &container)
|
|||
GatherLeafVolumes_r(nodedata->children[1], container);
|
||||
}
|
||||
|
||||
/* Returns true if the user requested to generate an entity bmodel clipnodes
|
||||
* for a given hull. */
|
||||
static bool ShouldGenerateClipnodes(mapentity_t &entity, hull_index_t hullnum)
|
||||
{
|
||||
// Default to generating clipnodes for all hulls.
|
||||
if (!entity.epairs.has("_hulls")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const int hulls = entity.epairs.get_int("_hulls");
|
||||
// Ensure 0 means all hulls even in the case we have more than 32 hulls.
|
||||
if (hulls == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return hulls & (1 << hullnum.value_or(0));
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
ProcessEntity
|
||||
|
|
@ -1086,6 +1104,22 @@ static void ProcessEntity(mapentity_t &entity, hull_index_t hullnum)
|
|||
return;
|
||||
}
|
||||
|
||||
// _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());
|
||||
} 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;
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
}
|
||||
|
|
@ -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();
|
||||
|
|
|
|||
Loading…
Reference in New Issue