diff --git a/docs/qbsp.rst b/docs/qbsp.rst index ab3c85db..119401a9 100644 --- a/docs/qbsp.rst +++ b/docs/qbsp.rst @@ -819,6 +819,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. diff --git a/qbsp/qbsp.cc b/qbsp/qbsp.cc index 98019483..87b57c64 100644 --- a/qbsp/qbsp.cc +++ b/qbsp/qbsp.cc @@ -985,6 +985,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 @@ -1089,6 +1107,16 @@ static void ProcessEntity(mapentity_t &entity, hull_index_t hullnum) // 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); + ExportClipNodes(entity, tree.headnode, hullnum.value()); + return; + } + tree_t tree; BrushBSP(tree, entity, brushes, tree_split_t::FAST); if (map.is_world_entity(entity) && !qbsp_options.nofill.value()) { @@ -1117,6 +1145,10 @@ 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;