diff --git a/common/bspfile.cc b/common/bspfile.cc index 0c8e6d90..e337b200 100644 --- a/common/bspfile.cc +++ b/common/bspfile.cc @@ -85,9 +85,11 @@ struct gamedef_generic_t : public gamedef_t bool contents_are_detail_illusionary(const contentflags_t &contents) const { throw std::bad_cast(); } bool contents_are_empty(const contentflags_t &) const { throw std::bad_cast(); } - + bool contents_are_mirrored(const contentflags_t &) const { throw std::bad_cast(); } + bool contents_clip_same_type(const contentflags_t &, const contentflags_t &) const { throw std::bad_cast(); } + bool contents_are_solid(const contentflags_t &) const { throw std::bad_cast(); } bool contents_are_sky(const contentflags_t &) const { throw std::bad_cast(); } @@ -262,7 +264,7 @@ struct gamedef_q1_like_t : public gamedef_t // if we have mirrorinside set, go ahead if (contents.mirror_inside.has_value()) { return contents.mirror_inside.value(); - } + } // If the brush is non-solid, mirror faces for the inside view return (contents.native == CONTENTS_WATER) @@ -270,6 +272,11 @@ struct gamedef_q1_like_t : public gamedef_t || (contents.native == CONTENTS_LAVA); } + bool contents_clip_same_type(const contentflags_t &self, const contentflags_t &other) const + { + return self == other && self.clips_same_type.value_or(true); + } + bool contents_are_empty(const contentflags_t &contents) const { if (contents.extended & CFLAGS_CONTENTS_MASK) @@ -665,6 +672,11 @@ struct gamedef_q2_t : public gamedef_t // contents to default to not mirroring the insides. return !(contents.native & (Q2_CONTENTS_SOLID | Q2_CONTENTS_AUX)); } + + bool contents_clip_same_type(const contentflags_t &self, const contentflags_t &other) const + { + return (self.native & Q2_ALL_VISIBLE_CONTENTS) == (other.native & Q2_ALL_VISIBLE_CONTENTS) && self.clips_same_type.value_or(true); + } bool contents_are_empty(const contentflags_t &contents) const { @@ -1152,6 +1164,11 @@ bool contentflags_t::is_mirrored(const gamedef_t *game) const return game->contents_are_mirrored(*this); } +bool contentflags_t::will_clip_same_type(const gamedef_t *game, const contentflags_t &other) const +{ + return game->contents_clip_same_type(*this, other); +} + bool contentflags_t::is_empty(const gamedef_t *game) const { return game->contents_are_empty(*this); @@ -1182,11 +1199,10 @@ std::string contentflags_t::to_string(const gamedef_t *game) const std::string s = game->get_contents_display(*this); // FIXME: how do we conditionally display this only when it matters (when it's not default basically)? - s += fmt::format("|MIRROR_INSIDE[{}]", mirror_inside.has_value() ? (mirror_inside.value() ? "true" : "false") : "nullopt"); + s += fmt::format("|MIRROR_INSIDE[{}]", mirror_inside.has_value() ? (clips_same_type.value() ? "true" : "false") : "nullopt"); + + s += fmt::format("|CLIPS_SAME_TYPE[{}]", clips_same_type.has_value() ? (mirror_inside.value() ? "true" : "false") : "nullopt"); - if (extended & CFLAGS_NO_CLIPPING_SAME_TYPE) { - s += "|NO_CLIPPING_SAME_TYPE"; - } if (extended & CFLAGS_HINT) { s += "|HINT"; } diff --git a/include/common/bspfile.hh b/include/common/bspfile.hh index 45e1d6a9..e72889b3 100644 --- a/include/common/bspfile.hh +++ b/include/common/bspfile.hh @@ -563,7 +563,6 @@ enum q2_contents_t : int32_t // Special contents flags for the compiler only enum extended_cflags_t : uint16_t { - CFLAGS_NO_CLIPPING_SAME_TYPE = nth_bit(4), /* Don't clip the same content type. mostly intended for CONTENTS_DETAIL_ILLUSIONARY */ // only one of these flags below should ever be set. CFLAGS_HINT = nth_bit(5), CFLAGS_CLIP = nth_bit(6), @@ -590,9 +589,13 @@ struct contentflags_t uint16_t extended; // the value set directly from `_mirrorinside` on the brush, if available. - // don't use this directly, use `is_mirror_inside` to allow the game to decide. + // don't check this directly, use `is_mirror_inside` to allow the game to decide. std::optional mirror_inside = std::nullopt; + // Don't clip the same content type. mostly intended for CONTENTS_DETAIL_ILLUSIONARY. + // don't check this directly, use `will_clip_same_type` to allow the game to decide. + std::optional clips_same_type = std::nullopt; + constexpr bool operator==(const contentflags_t &other) const { return native == other.native && extended == other.extended; @@ -605,9 +608,13 @@ struct contentflags_t bool is_detail_solid(const gamedef_t *game) const; bool is_detail_fence(const gamedef_t *game) const; bool is_detail_illusionary(const gamedef_t *game) const; - + bool is_mirrored(const gamedef_t *game) const; contentflags_t &set_mirrored(const std::optional &mirror_inside_value) { mirror_inside = mirror_inside_value; return *this; } + + inline bool will_clip_same_type(const gamedef_t *game) const { return will_clip_same_type(game, *this); } + bool will_clip_same_type(const gamedef_t *game, const contentflags_t &other) const; + contentflags_t &set_clips_same_type(const std::optional &clips_same_type_value) { clips_same_type = clips_same_type_value; return *this; } bool is_empty(const gamedef_t *game) const; @@ -629,8 +636,6 @@ struct contentflags_t constexpr bool is_origin() const { return extended & CFLAGS_ORIGIN; } - constexpr bool clips_same_type() const { return !(extended & CFLAGS_NO_CLIPPING_SAME_TYPE); } - bool is_fence(const gamedef_t *game) const { return is_detail_fence(game) || is_detail_illusionary(game); } @@ -1822,6 +1827,7 @@ struct gamedef_t virtual bool contents_are_detail_illusionary(const contentflags_t &contents) const = 0; virtual bool contents_are_mirrored(const contentflags_t &contents) const = 0; virtual bool contents_are_empty(const contentflags_t &contents) const = 0; + virtual bool contents_clip_same_type(const contentflags_t &self, const contentflags_t &other) const = 0; virtual bool contents_are_solid(const contentflags_t &contents) const = 0; virtual bool contents_are_sky(const contentflags_t &contents) const = 0; virtual bool contents_are_liquid(const contentflags_t &contents) const = 0; diff --git a/qbsp/brush.cc b/qbsp/brush.cc index 159df599..97d1bb08 100644 --- a/qbsp/brush.cc +++ b/qbsp/brush.cc @@ -862,7 +862,8 @@ static void Brush_LoadEntity(mapentity_t *dst, const mapentity_t *src, const int const std::optional mirrorinside = mirrorinside_set ? decltype(mirrorinside)(atoi(ValueForKey(src, "_mirrorinside")) ? true : false) : std::nullopt; /* _noclipfaces */ - const bool noclipfaces = !!atoi(ValueForKey(src, "_noclipfaces")); + const bool noclipfaces_set = *ValueForKey(src, "_mirrorinside"); + const std::optional noclipfaces = mirrorinside_set ? decltype(noclipfaces)(atoi(ValueForKey(src, "_noclipfaces")) ? false : true) : std::nullopt; const bool func_illusionary_visblocker = (0 == Q_strcasecmp(classname, "func_illusionary_visblocker")); @@ -969,10 +970,7 @@ static void Brush_LoadEntity(mapentity_t *dst, const mapentity_t *src, const int // apply extended flags contents.set_mirrored(mirrorinside); - - if (noclipfaces) { - contents.extended |= CFLAGS_NO_CLIPPING_SAME_TYPE; - } + contents.set_clips_same_type(noclipfaces); if (func_illusionary_visblocker) { contents.extended |= CFLAGS_ILLUSIONARY_VISBLOCKER; } diff --git a/qbsp/csg4.cc b/qbsp/csg4.cc index 994e6baa..30b9d9b4 100644 --- a/qbsp/csg4.cc +++ b/qbsp/csg4.cc @@ -262,7 +262,7 @@ static bool ShouldClipbrushEatBrush(const brush_t &brush, const brush_t &clipbru } if (clipbrush.contents.types_equal(brush.contents, options.target_game)) { - return clipbrush.contents.clips_same_type(); + return clipbrush.contents.will_clip_same_type(options.target_game); } return false; @@ -426,7 +426,7 @@ Returns a >= b as far as brush clipping bool BrushGE(const brush_t& a, const brush_t& b) { // same contents clip each other - if (a.contents == b.contents && a.contents.clips_same_type()) { + if (a.contents == b.contents && a.contents.will_clip_same_type(options.target_game)) { // map file order return a.file_order > b.file_order; } diff --git a/qbsp/surfaces.cc b/qbsp/surfaces.cc index 50c26922..40861397 100644 --- a/qbsp/surfaces.cc +++ b/qbsp/surfaces.cc @@ -574,11 +574,11 @@ static std::list ClipFacesToTree_r(node_t *node, const brush_t *srcbru return {}; } - // translucent contents also clip faces - if (node->contents == srcbrush->contents - && srcbrush->contents.clips_same_type()) { + // see what the game thinks about the clip + if (srcbrush->contents.will_clip_same_type(options.target_game, node->contents)) { return {}; } + // other content types let the faces thorugh return faces; }