Implement game-agnostic Brush_GetContents

This commit is contained in:
Jonathan 2021-10-26 09:13:48 -04:00
parent 6856789eef
commit c37ec80667
6 changed files with 105 additions and 103 deletions

View File

@ -33,7 +33,7 @@ struct gamedef_generic_t : public gamedef_t
bool surf_is_subdivided(const surfflags_t &) const { throw std::bad_cast(); }
bool surfflags_are_valid(const surfflags_t &flags) const { throw std::bad_cast(); }
bool surfflags_are_valid(const surfflags_t &) const { throw std::bad_cast(); }
contentflags_t cluster_contents(const contentflags_t &, const contentflags_t &) const { throw std::bad_cast(); }
@ -51,11 +51,11 @@ struct gamedef_generic_t : public gamedef_t
contentflags_t create_liquid_contents(const int32_t &, const int32_t &) const { throw std::bad_cast(); }
bool contents_are_empty(const contentflags_t &contents) const { throw std::bad_cast(); }
bool contents_are_empty(const contentflags_t &) const { throw std::bad_cast(); }
bool contents_are_solid(const contentflags_t &contents) const { throw std::bad_cast(); }
bool contents_are_solid(const contentflags_t &) const { throw std::bad_cast(); }
bool contents_are_sky(const contentflags_t &contents) const { throw std::bad_cast(); }
bool contents_are_sky(const contentflags_t &) const { throw std::bad_cast(); }
bool contents_are_liquid(const contentflags_t &) const { throw std::bad_cast(); }
@ -63,9 +63,11 @@ struct gamedef_generic_t : public gamedef_t
bool portal_can_see_through(const contentflags_t &, const contentflags_t &) const { throw std::bad_cast(); }
std::string get_contents_display(const contentflags_t &contents) const { throw std::bad_cast(); }
std::string get_contents_display(const contentflags_t &) const { throw std::bad_cast(); }
const std::initializer_list<aabb3d> &get_hull_sizes() const { throw std::bad_cast(); }
contentflags_t face_get_contents(const std::string &, const surfflags_t &, const contentflags_t &) const { throw std::bad_cast(); };
};
template<gameid_t ID>
@ -245,6 +247,31 @@ struct gamedef_q1_like_t : public gamedef_t
return hulls;
}
contentflags_t face_get_contents(const std::string &texname, const surfflags_t &flags, const contentflags_t &) const
{
// check for strong content indicators
if (!Q_strcasecmp(texname.data(), "origin")) {
return create_extended_contents(CFLAGS_ORIGIN);
} else if (!Q_strcasecmp(texname.data(), "hint")) {
return create_extended_contents(CFLAGS_HINT);
} else if (!Q_strcasecmp(texname.data(), "clip")) {
return create_extended_contents(CFLAGS_CLIP);
} else if (texname[0] == '*') {
if (!Q_strncasecmp(texname.data() + 1, "lava", 4)) {
return create_liquid_contents(CONTENTS_LAVA);
} else if (!Q_strncasecmp(texname.data() + 1, "slime", 5)) {
return create_liquid_contents(CONTENTS_SLIME);
} else {
return create_liquid_contents(CONTENTS_WATER);
}
} else if (!Q_strncasecmp(texname.data(), "sky", 3)) {
return create_sky_contents();
} else {
// and anything else is assumed to be a regular solid.
return create_solid_contents();
}
}
};
struct gamedef_h2_t : public gamedef_q1_like_t<GAME_HEXEN_II>
@ -312,7 +339,7 @@ struct gamedef_q2_t : public gamedef_t
int32_t get_content_type(const contentflags_t &contents) const
{
return (contents.native & ((Q2_LAST_VISIBLE_CONTENTS << 1) - 1)) |
(Q2_CONTENTS_PLAYERCLIP | Q2_CONTENTS_MONSTERCLIP);
(Q2_CONTENTS_PLAYERCLIP | Q2_CONTENTS_MONSTERCLIP | Q2_CONTENTS_ORIGIN | Q2_CONTENTS_DETAIL | Q2_CONTENTS_TRANSLUCENT | Q2_CONTENTS_AREAPORTAL);
}
int32_t contents_priority(const contentflags_t &contents) const
@ -457,6 +484,51 @@ struct gamedef_q2_t : public gamedef_t
static constexpr std::initializer_list<aabb3d> hulls = {};
return hulls;
}
contentflags_t face_get_contents(const std::string &texname, const surfflags_t &flags, const contentflags_t &contents) const
{
contentflags_t surf_contents = contents;
if (flags.native & (Q2_SURF_TRANS33 | Q2_SURF_TRANS66)) {
surf_contents.native |= Q2_CONTENTS_TRANSLUCENT;
if (surf_contents.native & Q2_CONTENTS_SOLID) {
surf_contents.native = (surf_contents.native & ~Q2_CONTENTS_SOLID) | Q2_CONTENTS_WINDOW;
}
}
// add extended flags that we may need
if (surf_contents.native & Q2_CONTENTS_DETAIL) {
surf_contents.extended |= CFLAGS_DETAIL;
}
if (surf_contents.native & (Q2_CONTENTS_MONSTERCLIP | Q2_CONTENTS_PLAYERCLIP)) {
surf_contents.extended |= CFLAGS_CLIP;
}
if (surf_contents.native & Q2_CONTENTS_ORIGIN) {
surf_contents.extended |= CFLAGS_ORIGIN;
}
if (surf_contents.native & Q2_CONTENTS_MIST) {
surf_contents.extended |= CFLAGS_DETAIL_ILLUSIONARY;
}
if (flags.native & Q2_SURF_HINT) {
surf_contents.extended |= CFLAGS_HINT;
}
// FIXME: this is a bit of a hack, but this is because clip
// and liquids and stuff are already handled *like* detail by
// the compiler.
if (surf_contents.extended & CFLAGS_DETAIL) {
if (!(surf_contents.native & Q2_CONTENTS_SOLID)) {
surf_contents.extended &= ~CFLAGS_DETAIL;
}
}
return surf_contents;
}
};
// Game definitions, used for the bsp versions below

View File

@ -1762,6 +1762,7 @@ struct gamedef_t
virtual bool portal_can_see_through(const contentflags_t &contents0, const contentflags_t &contents1) const = 0;
virtual std::string get_contents_display(const contentflags_t &contents) const = 0;
virtual const std::initializer_list<aabb3d> &get_hull_sizes() const = 0;
virtual contentflags_t face_get_contents(const std::string &texname, const surfflags_t &flags, const contentflags_t &contents) const = 0;
};
// BSP version struct & instances

View File

@ -48,7 +48,7 @@ struct mapface_t
surfflags_t flags { };
// Q2 stuff
int contents = 0;
contentflags_t contents { };
int value = 0;
bool set_planepts(const std::array<qvec3d, 3> &pts);

View File

@ -107,7 +107,6 @@ enum
#include <common/mathlib.hh>
#include <qbsp/winding.hh>
struct mtexinfo_t
{
texvecf vecs; /* [s/t][xyz offset] */

View File

@ -691,53 +691,30 @@ static void ExpandBrush(hullbrush_t *hullbrush, const aabb3d &hull_size, const f
static const int DetailFlag = (1 << 27);
// FIXME: move this to earlier into the map process, like in
// texdef parsing or something
static bool Brush_IsDetail(const mapbrush_t *mapbrush)
{
const mapface_t &mapface = mapbrush->face(0);
if ((mapface.contents & DetailFlag) == DetailFlag) {
if ((mapface.contents.native & DetailFlag) == DetailFlag) {
return true;
}
return false;
}
static contentflags_t Brush_GetContents_Q1(const mapbrush_t *mapbrush)
static contentflags_t Brush_GetContents(const mapbrush_t *mapbrush)
{
const char *texname;
// check for strong content indicators
for (int i = 0; i < mapbrush->numfaces; i++) {
const mapface_t &mapface = mapbrush->face(i);
// use the first side as the base contents value
contentflags_t base_contents;
{
const mapface_t &mapface = mapbrush->face(0);
const mtexinfo_t &texinfo = map.mtexinfos.at(mapface.texinfo);
texname = map.miptexTextureName(texinfo.miptex).c_str();
if (!Q_strcasecmp(texname, "origin"))
return options.target_game->create_extended_contents(CFLAGS_ORIGIN);
else if (!Q_strcasecmp(texname, "hint"))
return options.target_game->create_extended_contents(CFLAGS_HINT);
else if (!Q_strcasecmp(texname, "clip"))
return options.target_game->create_extended_contents(CFLAGS_CLIP);
else if (texname[0] == '*') {
if (!Q_strncasecmp(texname + 1, "lava", 4))
return options.target_game->create_liquid_contents(CONTENTS_LAVA);
else if (!Q_strncasecmp(texname + 1, "slime", 5))
return options.target_game->create_liquid_contents(CONTENTS_SLIME);
else
return options.target_game->create_liquid_contents(CONTENTS_WATER);
} else if (!Q_strncasecmp(texname, "sky", 3))
return options.target_game->create_sky_contents();
base_contents = options.target_game->face_get_contents(mapface.texname.data(), texinfo.flags, mapface.contents);
}
// and anything else is assumed to be a regular solid.
return options.target_game->create_solid_contents();
}
static contentflags_t Brush_GetContents_Q2(const mapbrush_t *mapbrush)
{
bool is_trans = false;
bool is_hint = false;
contentflags_t contents = {mapbrush->face(0).contents};
// validate that all of the sides have valid contents
for (int i = 0; i < mapbrush->numfaces; i++) {
const mapface_t &mapface = mapbrush->face(i);
const mtexinfo_t &texinfo = map.mtexinfos.at(mapface.texinfo);
@ -746,64 +723,20 @@ static contentflags_t Brush_GetContents_Q2(const mapbrush_t *mapbrush)
continue;
}
if (!is_trans && (texinfo.flags.native & (Q2_SURF_TRANS33 | Q2_SURF_TRANS66))) {
is_trans = true;
}
contentflags_t contents = options.target_game->face_get_contents(mapface.texname.data(), texinfo.flags, mapface.contents);
if (!is_hint && (texinfo.flags.native & Q2_SURF_HINT)) {
is_hint = true;
}
if (mapface.contents != contents.native) {
LogPrint("mixed face contents ({} != {} at line {})\n",
if (!contents.types_equal(base_contents, options.target_game)) {
LogPrint("mixed face contents ({} != {}) at line {}\n",
contentflags_t{mapface.contents}.to_string(options.target_game),
contents.to_string(options.target_game), mapface.linenum);
break;
}
}
// make sure we found a valid type
Q_assert(base_contents.is_valid(options.target_game, false));
// if any side is translucent, mark the contents
// and change solid to window
if (is_trans) {
contents.native |= Q2_CONTENTS_TRANSLUCENT;
if (contents.native & Q2_CONTENTS_SOLID) {
contents.native = (contents.native & ~Q2_CONTENTS_SOLID) | Q2_CONTENTS_WINDOW;
}
}
// add extended flags that we may need
if (contents.native & Q2_CONTENTS_DETAIL) {
contents.extended |= CFLAGS_DETAIL;
}
if (contents.native & (Q2_CONTENTS_MONSTERCLIP | Q2_CONTENTS_PLAYERCLIP)) {
contents.extended |= CFLAGS_CLIP;
}
if (contents.native & Q2_CONTENTS_ORIGIN) {
contents.extended |= CFLAGS_ORIGIN;
}
if (contents.native & Q2_CONTENTS_MIST) {
contents.extended |= CFLAGS_DETAIL_ILLUSIONARY;
}
if (is_hint) {
contents.extended |= CFLAGS_HINT;
}
// FIXME: this is a bit of a hack, but this is because clip
// and liquids and stuff are already handled *like* detail by
// the compiler.
if (contents.extended & CFLAGS_DETAIL) {
if (!(contents.native & Q2_CONTENTS_SOLID)) {
contents.extended &= ~CFLAGS_DETAIL;
}
}
Q_assert(contents.is_valid(options.target_game, false));
return contents;
return base_contents;
}
/*
@ -979,9 +912,6 @@ void Brush_LoadEntity(mapentity_t *dst, const mapentity_t *src, const int hullnu
/* Origin brush support */
rotation_t rottype = rotation_t::none;
// TODO: move to game
auto Brush_GetContents = (options.target_game->id == GAME_QUAKE_II) ? Brush_GetContents_Q2 : Brush_GetContents_Q1;
for (int i = 0; i < src->nummapbrushes; i++) {
const mapbrush_t *mapbrush = &src->mapbrush(i);
const contentflags_t contents = Brush_GetContents(mapbrush);

View File

@ -1370,7 +1370,7 @@ static void ParseTextureDef(parser_t &parser, mapface_t &mapface, const mapbrush
tx->miptex = FindMiptex(mapface.texname.c_str(), extinfo.info);
mapface.contents = extinfo.info->contents;
mapface.contents = { extinfo.info->contents };
tx->flags = mapface.flags = {extinfo.info->flags};
tx->value = mapface.value = extinfo.info->value;
@ -1546,18 +1546,18 @@ mapbrush_t ParseBrush(parser_t &parser, const mapentity_t *entity)
if (options.target_game->id == GAME_QUAKE_II) {
// translucent objects are automatically classified as detail
if ((face->flags.native & (Q2_SURF_TRANS33 | Q2_SURF_TRANS66)) ||
(face->contents & (Q2_CONTENTS_PLAYERCLIP | Q2_CONTENTS_MONSTERCLIP))) {
face->contents |= Q2_CONTENTS_DETAIL;
(face->contents.native & (Q2_CONTENTS_PLAYERCLIP | Q2_CONTENTS_MONSTERCLIP))) {
face->contents.native |= Q2_CONTENTS_DETAIL;
}
if (!(face->contents &
if (!(face->contents.native &
(((Q2_LAST_VISIBLE_CONTENTS << 1) - 1) | Q2_CONTENTS_PLAYERCLIP | Q2_CONTENTS_MONSTERCLIP))) {
face->contents |= Q2_CONTENTS_SOLID;
face->contents.native |= Q2_CONTENTS_SOLID;
}
// hints and skips are never detail, and have no content
if (face->flags.native & (Q2_SURF_HINT | Q2_SURF_SKIP)) {
face->contents = 0;
face->contents.native = 0;
}
}
@ -1789,7 +1789,7 @@ void ProcessAreaPortal(mapentity_t *entity)
FError("func_areaportal can only be a single brush");
map.brushes[entity->firstmapbrush].contents = Q2_CONTENTS_AREAPORTAL;
map.faces[map.brushes[entity->firstmapbrush].firstface].contents = Q2_CONTENTS_AREAPORTAL;
map.faces[map.brushes[entity->firstmapbrush].firstface].contents.native = Q2_CONTENTS_AREAPORTAL;
entity->areaportalnum = ++map.numareaportals;
// set the portal number as "style"
SetKeyValue(entity, "style", std::to_string(map.numareaportals).c_str());