From d2ae49c97633a4bf4217f6fae82fdfa27f7cf718 Mon Sep 17 00:00:00 2001 From: Eric Wasylishen Date: Sat, 16 Apr 2022 02:09:11 -0600 Subject: [PATCH] qbsp: fix void faces in qbsp_simple_sealed2.map --- qbsp/surfaces.cc | 68 +++++++++++++++++++++++++++++++++++++++++++++-- qbsp/test_qbsp.cc | 7 +++++ 2 files changed, 73 insertions(+), 2 deletions(-) diff --git a/qbsp/surfaces.cc b/qbsp/surfaces.cc index 8067da57..908ee09f 100644 --- a/qbsp/surfaces.cc +++ b/qbsp/surfaces.cc @@ -523,6 +523,66 @@ void MakeMarkFaces(mapentity_t* entity, node_t* node) MakeMarkFaces(entity, node->children[1]); } +static std::list ClipFacesToTree_r(node_t *node, std::list faces) +{ + if (node->planenum == PLANENUM_LEAF) { + if (node->contents.is_solid(options.target_game)) { + // solids eat any faces that reached this point + return {}; + } + // other content types let the faces thorugh + // fixme-brushbsp: detail? + return faces; + } + + const qbsp_plane_t &splitplane = map.planes[node->planenum]; + + std::list front, back; + + for (auto *face : faces) { + auto [frontFragment, backFragment] = SplitFace(face, splitplane); + if (frontFragment) { + front.push_back(frontFragment); + } + if (backFragment) { + back.push_back(backFragment); + } + } + + front = ClipFacesToTree_r(node->children[0], front); + back = ClipFacesToTree_r(node->children[1], back); + + // merge lists + front.splice(front.end(), back); + + return front; +} + +static std::list ClipFacesToTree(node_t* node, std::list faces) +{ + // handles the first level - faces are all supposed to be lying exactly on `node` + for (auto *face : faces) { + Q_assert(face->planenum == node->planenum); + } + + std::list front, back; + for (auto *face : faces) { + if (face->planeside == 0) { + front.push_back(face); + } else { + back.push_back(face); + } + } + + front = ClipFacesToTree_r(node->children[0], front); + back = ClipFacesToTree_r(node->children[1], back); + + // merge lists + front.splice(front.end(), back); + + return front; +} + static void AddFaceToTree_r(mapentity_t* entity, face_t *face, brush_t *srcbrush, node_t* node) { if (node->planenum == PLANENUM_LEAF) { @@ -537,9 +597,13 @@ static void AddFaceToTree_r(mapentity_t* entity, face_t *face, brush_t *srcbrush ++c_nodefaces; // csg it - auto csgfaces = CSGFace(face, entity, srcbrush, node); + std::list faces = CSGFace(face, entity, srcbrush, node); - for (face_t *csgface : csgfaces) { + // clip them to the descendant parts of the BSP + // (otherwise we could have faces floating in the void on this node) + faces = ClipFacesToTree(node, faces); + + for (face_t *csgface : faces) { // subdivide large faces // fixme-brushbsp: weird calling convention auto parts = std::list{csgface}; diff --git a/qbsp/test_qbsp.cc b/qbsp/test_qbsp.cc index e590bbbb..932c7142 100644 --- a/qbsp/test_qbsp.cc +++ b/qbsp/test_qbsp.cc @@ -40,6 +40,9 @@ static mbsp_t LoadTestmap(const std::filesystem::path &name) { map.reset(); + options.nopercent.setValue(true); + options.noprogress.setValue(true); + options.szMapName = std::filesystem::path(testmaps_dir) / name; options.szBSPName = options.szMapName; options.szBSPName.replace_extension(".bsp"); @@ -265,4 +268,8 @@ TEST(qsbsp, simple_sealed2) ASSERT_EQ(result.dleafs[0].contents, CONTENTS_SOLID); ASSERT_EQ(result.dleafs[1].contents, CONTENTS_EMPTY); ASSERT_EQ(result.dleafs[2].contents, CONTENTS_EMPTY); + + // L-shaped room + // 2 ceiling + 2 floor + 6 wall faces + ASSERT_EQ(result.dfaces.size(), 10); }