Revert "qbsp: remove unused face csg code"

This reverts commit 2ad9e27d3b.
This commit is contained in:
Eric Wasylishen 2022-05-21 01:42:50 -06:00
parent cf91610738
commit 3b83508f37
3 changed files with 253 additions and 1 deletions

View File

@ -239,6 +239,7 @@ constexpr int HULL_COLLISION = -1;
/* Create BSP brushes from map brushes */
brush_stats_t Brush_LoadEntity(mapentity_t *entity, const int hullnum);
std::list<face_t *> 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);

View File

@ -149,6 +149,106 @@ std::tuple<face_t *, face_t *> SplitFace(face_t *in, const qplane3d &split)
return {new_front, new_back};
}
/*
=================
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<face_t *> *inside, std::list<face_t *> *outside)
{
std::list<face_t *> oldinside;
// clear `inside`, transfer it to `oldinside`
std::swap(*inside, oldinside);
for (face_t *face : oldinside) {
std::optional<winding_t> 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<face_t *> *inside, std::list<face_t *> *outside)
{
std::list<face_t *> 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<size_t, SIDE_TOTAL> 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<face_t *, 2> 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);
@ -177,6 +277,157 @@ static std::vector<std::unique_ptr<brush_t>> SingleBrush(std::unique_ptr<brush_t
return res;
}
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<face_t *> CSGFace_ClipAgainstSingleBrush(std::list<face_t *> 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 = srcbrush->file_order;
const int clipindex = clipbrush->file_order;
if (!ShouldClipbrushEatBrush(*srcbrush, *clipbrush)) {
return {input};
}
std::list<face_t *> inside {input};
std::list<face_t *> 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<const brush_t *> &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<const brush_t *> GatherPossibleClippingBrushes(const mapentity_t* srcentity, const node_t *node, const face_t *srcface)
{
std::set<const brush_t *> 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.get());
}
#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<face_t *> 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<face_t *> result{srcface};
for (const brush_t *possible_clipbrush : possible_clipbrushes) {
result = CSGFace_ClipAgainstSingleBrush(std::move(result), srcentity, srcbrush, possible_clipbrush);
}
return result;
}
/*
==================
SubtractBrush

View File

@ -645,7 +645,7 @@ static void AddFaceToTree_r(mapentity_t* entity, face_t *face, brush_t *srcbrush
++c_nodefaces;
// csg it
std::list<face_t *> faces{face};
std::list<face_t *> faces = CSGFace(face, entity, srcbrush, node);
// clip them to the descendant parts of the BSP
// (otherwise we could have faces floating in the void on this node)