diff --git a/include/qbsp/map.hh b/include/qbsp/map.hh index 2ee2fcb1..f4dc9421 100644 --- a/include/qbsp/map.hh +++ b/include/qbsp/map.hh @@ -224,7 +224,6 @@ constexpr int HULL_COLLISION = -1; /* Create BSP brushes from map brushes */ brush_stats_t Brush_LoadEntity(mapentity_t *entity, const int hullnum); -std::list CSGFace(face_t *srcface, const mapentity_t* srcentity, const brush_t *srcbrush, const node_t *srcnode); void TJunc(const mapentity_t *entity, node_t *headnode); int MakeFaceEdges(mapentity_t *entity, node_t *headnode); void ExportClipNodes(mapentity_t *entity, node_t *headnode, const int hullnum); diff --git a/qbsp/csg4.cc b/qbsp/csg4.cc index 845b9977..33ea1665 100644 --- a/qbsp/csg4.cc +++ b/qbsp/csg4.cc @@ -187,106 +187,6 @@ std::tuple SplitFace(face_t *in, const qplane3d &split) return {new2, newf}; } -/* -================= -RemoveOutsideFaces - -Quick test before running ClipInside; move any faces that are completely -outside the brush to the outside list, without splitting them. This saves us -time in mergefaces later on (and sometimes a lot of memory) - -Input is a list of faces in the param `inside`. -On return, the ones touching `brush` remain in `inside`, the rest are added to `outside`. -================= -*/ -static void RemoveOutsideFaces(const brush_t &brush, std::list *inside, std::list *outside) -{ - std::list oldinside; - - // clear `inside`, transfer it to `oldinside` - std::swap(*inside, oldinside); - - for (face_t *face : oldinside) { - std::optional w = face->w; - for (auto &clipface : brush.faces) { - w = w->clip(Face_Plane(&clipface), ON_EPSILON, false)[SIDE_BACK]; - if (!w) - break; - } - if (!w) { - /* The face is completely outside this brush */ - outside->push_front(face); - } else { - inside->push_front(face); - } - } -} - -/* -================= -ClipInside - -Clips all of the faces in the inside list, possibly moving them to the -outside list or spliting it into a piece in each list. - -Faces exactly on the plane will stay inside unless overdrawn by later brush -================= -*/ -static void ClipInside( - const face_t *clipface, bool precedence, std::list *inside, std::list *outside) -{ - std::list oldinside; - - // effectively make a copy of `inside`, and clear it - std::swap(*inside, oldinside); - - const qbsp_plane_t &splitplane = map.planes[clipface->planenum]; - - for (face_t *face : oldinside) { - /* HACK: Check for on-plane but not the same planenum - ( https://github.com/ericwa/ericw-tools/issues/174 ) - */ - bool spurious_onplane = false; - { - std::array counts = face->w.calc_sides(splitplane, nullptr, nullptr, ON_EPSILON); - - if (counts[SIDE_ON] && !counts[SIDE_FRONT] && !counts[SIDE_BACK]) { - spurious_onplane = true; - } - } - - std::array frags; - - /* Handle exactly on-plane faces */ - if (face->planenum == clipface->planenum || spurious_onplane) { - const qplane3d faceplane = Face_Plane(face); - const qplane3d clipfaceplane = Face_Plane(clipface); - const vec_t dp = qv::dot(faceplane.normal, clipfaceplane.normal); - const bool opposite = (dp < 0); - - if (opposite || precedence) { - /* always clip off opposite facing */ - frags[clipface->planeside] = {}; - frags[!clipface->planeside] = {face}; - } else { - /* leave it on the outside */ - frags[clipface->planeside] = {face}; - frags[!clipface->planeside] = {}; - } - } else { - /* proper split */ - std::tie(frags[0], frags[1]) = SplitFace(face, splitplane); - } - - if (frags[clipface->planeside]) { - outside->push_front(frags[clipface->planeside]); - } - if (frags[!clipface->planeside]) { - inside->push_front(frags[!clipface->planeside]); - } - } -} - face_t *MirrorFace(const face_t *face) { face_t *newface = NewFaceFromFace(face); @@ -325,157 +225,6 @@ static int BrushIndexInMap(const mapentity_t *entity, const brush_t *brush) return static_cast(brush - entity->brushes.data()); } -static bool ShouldClipbrushEatBrush(const brush_t &brush, const brush_t &clipbrush) -{ - if (clipbrush.contents.is_empty(options.target_game)) { - /* Ensure hint never clips anything */ - return false; - } - - if (clipbrush.contents.is_detail(CFLAGS_DETAIL_ILLUSIONARY) && - !brush.contents.is_detail(CFLAGS_DETAIL_ILLUSIONARY)) { - /* CONTENTS_DETAIL_ILLUSIONARY never clips anything but itself */ - return false; - } - - if (clipbrush.contents.is_detail(CFLAGS_DETAIL_FENCE) && !brush.contents.is_detail(CFLAGS_DETAIL_FENCE)) { - /* CONTENTS_DETAIL_FENCE never clips anything but itself */ - return false; - } - - if (clipbrush.contents.types_equal(brush.contents, options.target_game) && - !clipbrush.contents.clips_same_type()) { - /* _noclipfaces key */ - return false; - } - - /* - * If the brush is solid and the clipbrush is not, then we need to - * keep the inside faces and set the outside contents to those of - * the clipbrush. Otherwise, these inside surfaces are hidden and - * should be discarded. - * - * FIXME: clean this up, the predicate seems to be "can you see 'brush' from inside 'clipbrush'" - */ - if ((brush.contents.is_solid(options.target_game) && !clipbrush.contents.is_solid(options.target_game)) - - || - (brush.contents.is_sky(options.target_game) && (!clipbrush.contents.is_solid(options.target_game) && - !clipbrush.contents.is_sky(options.target_game))) - - || (brush.contents.is_detail(CFLAGS_DETAIL) && (!clipbrush.contents.is_solid(options.target_game) && - !clipbrush.contents.is_sky(options.target_game) && - !clipbrush.contents.is_detail(CFLAGS_DETAIL))) - - || (brush.contents.is_liquid(options.target_game) && - clipbrush.contents.is_detail(CFLAGS_DETAIL_ILLUSIONARY)) - - || (brush.contents.is_fence() && clipbrush.contents.is_liquid(options.target_game))) { - return false; - } - - return true; -} - -static std::list CSGFace_ClipAgainstSingleBrush(std::list input, const mapentity_t *srcentity, const brush_t *srcbrush, const brush_t *clipbrush) -{ - if (srcbrush == clipbrush) { - logging::print(" ignoring self-clip\n"); - return input; - } - - const int srcindex = BrushIndexInMap(srcentity, srcbrush); - const int clipindex = BrushIndexInMap(srcentity, clipbrush); - - if (!ShouldClipbrushEatBrush(*srcbrush, *clipbrush)) { - return {input}; - } - - std::list inside {input}; - std::list outside; - RemoveOutsideFaces(*clipbrush, &inside, &outside); - - // at this point, inside = the faces of `input` which are touching `clipbrush` - // outside = the other faces of `input` - - const bool overwrite = (srcindex < clipindex); - - for (auto &clipface : clipbrush->faces) - ClipInside(&clipface, overwrite, &inside, &outside); - - // inside = parts of `brush` that are inside `clipbrush` - // outside = parts of `brush` that are outside `clipbrush` - - return outside; -} - -// fixme-brushbsp: determinism: sort `result` set by .map file order -// fixme-brushbsp: add bounds test -#if 0 -static void GatherPossibleClippingBrushes_R(const node_t *node, const face_t *srcface, std::set &result) -{ - if (node->planenum == PLANENUM_LEAF) { - for (auto *brush : node->original_brushes) { - result.insert(brush); - } - return; - } - - GatherPossibleClippingBrushes_R(node->children[0], srcface, result); - GatherPossibleClippingBrushes_R(node->children[1], srcface, result); -} -#endif - -/* -================== -GatherPossibleClippingBrushes - -Starting a search at `node`, returns brushes that possibly intersect `srcface`. -================== -*/ -static std::set GatherPossibleClippingBrushes(const mapentity_t* srcentity, const node_t *node, const face_t *srcface) -{ - std::set result; - // fixme-brushbsp: implement this, need node->original_brushes working -#if 0 - GatherPossibleClippingBrushes_R(node, srcface, result); -#else - for (auto &brush : srcentity->brushes) { - result.insert(&brush); - } -#endif - return result; -} - -/* -================== -CSGFace - -Given `srcface`, which was produced from `srcbrush` and lies on `srcnode`: - - - search srcnode as well as its children for brushes which might clip - srcface. - - - clip srcface against all such brushes - -Frees srcface. -================== -*/ -std::list CSGFace(face_t *srcface, const mapentity_t *srcentity, const brush_t *srcbrush, const node_t *srcnode) -{ - const auto possible_clipbrushes = GatherPossibleClippingBrushes(srcentity, srcnode, srcface); - - logging::print("face {} has {} possible clipbrushes\n", (void *)srcface, possible_clipbrushes.size()); - - std::list result{srcface}; - - for (const brush_t *possible_clipbrush : possible_clipbrushes) { - result = CSGFace_ClipAgainstSingleBrush(std::move(result), srcentity, srcbrush, possible_clipbrush); - } - - return result; -} - /* ================== SubtractBrush diff --git a/qbsp/surfaces.cc b/qbsp/surfaces.cc index 39618e0e..f5ac127c 100644 --- a/qbsp/surfaces.cc +++ b/qbsp/surfaces.cc @@ -614,7 +614,7 @@ static void AddFaceToTree_r(mapentity_t* entity, face_t *face, brush_t *srcbrush ++c_nodefaces; // csg it - std::list faces = CSGFace(face, entity, srcbrush, node); + std::list faces{face}; // clip them to the descendant parts of the BSP // (otherwise we could have faces floating in the void on this node)