Mostly all native!!

This commit is contained in:
Jonathan 2021-09-09 00:26:17 -04:00
parent 004fcb57c1
commit 9800f4f738
12 changed files with 486 additions and 247 deletions

View File

@ -40,6 +40,38 @@ struct gamedef_generic_t : public gamedef_t {
contentflags_t cluster_contents(const contentflags_t &, const contentflags_t &) const {
throw std::bad_cast();
}
int32_t get_content_type(const contentflags_t &) const {
throw std::bad_cast();
}
int32_t contents_priority(const contentflags_t &) const {
throw std::bad_cast();
}
contentflags_t create_empty_contents(const int32_t &) const {
throw std::bad_cast();
}
contentflags_t create_solid_contents(const int32_t &) const {
throw std::bad_cast();
}
contentflags_t create_sky_contents(const int32_t &) const {
throw std::bad_cast();
}
contentflags_t create_liquid_contents(const int32_t &, const int32_t &) const {
throw std::bad_cast();
}
bool contents_are_liquid(const contentflags_t &) const {
throw std::bad_cast();
}
bool contents_are_valid(const contentflags_t &, bool) const {
throw std::bad_cast();
}
};
template<gameid_t id>
@ -63,21 +95,78 @@ struct gamedef_q1_like_t : public gamedef_t {
if (contents0 == contents1)
return contents0;
const int32_t merged_flags = contents0.extended | contents1.extended;
/*
* Clusters may be partially solid but still be seen into
* ?? - Should we do something more explicit with mixed liquid contents?
*/
if (contents0.native == CONTENTS_EMPTY || contents1.native == CONTENTS_EMPTY)
return { CONTENTS_EMPTY, (uint16_t) (contents0.extended | contents1.extended) };
return create_empty_contents(merged_flags);
if (contents0.native >= CONTENTS_LAVA && contents0.native <= CONTENTS_WATER)
return { contents0.native, (uint16_t) (contents0.extended | contents1.extended) };
return create_liquid_contents(contents0.native, merged_flags);
if (contents1.native >= CONTENTS_LAVA && contents1.native <= CONTENTS_WATER)
return { contents1.native, (uint16_t) (contents0.extended | contents1.extended) };
return create_liquid_contents(contents1.native, merged_flags);
if (contents0.native == CONTENTS_SKY || contents1.native == CONTENTS_SKY)
return { CONTENTS_SKY, (uint16_t) (contents0.extended | contents1.extended) };
return create_sky_contents(merged_flags);
return { CONTENTS_SOLID, (uint16_t) (contents0.extended | contents1.extended) };
return create_solid_contents(merged_flags);
}
int32_t get_content_type(const contentflags_t &contents) const {
return contents.native;
}
int32_t contents_priority(const contentflags_t &contents) const {
if (contents.extended & CFLAGS_DETAIL) {
return 5;
} else if (contents.extended & CFLAGS_DETAIL_ILLUSIONARY) {
return 3;
} else if (contents.extended & CFLAGS_DETAIL_FENCE) {
return 4;
} else if (contents.extended & CFLAGS_ILLUSIONARY_VISBLOCKER) {
return 2;
} else switch( contents.native ) {
case CONTENTS_SOLID: return 7;
case CONTENTS_SKY: return 6;
case CONTENTS_WATER: return 2;
case CONTENTS_SLIME: return 2;
case CONTENTS_LAVA: return 2;
case CONTENTS_EMPTY: return 1;
case 0: return 0;
default:
Error("Bad contents in face (%s)", __func__);
return 0;
}
}
contentflags_t create_empty_contents(const int32_t &cflags) const {
return { CONTENTS_EMPTY, cflags };
}
contentflags_t create_solid_contents(const int32_t &cflags) const {
return { CONTENTS_SOLID, cflags };
}
contentflags_t create_sky_contents(const int32_t &cflags) const {
return { CONTENTS_SKY, cflags };
}
contentflags_t create_liquid_contents(const int32_t &liquid_type, const int32_t &cflags) const {
return { liquid_type, cflags };
}
bool contents_are_liquid(const contentflags_t &contents) const {
return contents.native <= CONTENTS_WATER && contents.native >= CONTENTS_LAVA;
}
bool contents_are_valid(const contentflags_t &contents, bool strict) const {
return contents.native <= 0;
}
};
@ -113,7 +202,7 @@ struct gamedef_q2_t : public gamedef_t {
}
contentflags_t cluster_contents(const contentflags_t &contents0, const contentflags_t &contents1) const {
contentflags_t c = { contents0.native | contents1.native, (uint16_t) (contents0.extended | contents1.extended) };
contentflags_t c = { contents0.native | contents1.native, contents0.extended | contents1.extended };
// a cluster may include some solid detail areas, but
// still be seen into
@ -122,6 +211,71 @@ struct gamedef_q2_t : public gamedef_t {
return c;
}
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);
}
int32_t contents_priority(const contentflags_t &contents) const {
if (contents.extended & CFLAGS_DETAIL) {
return 8;
} else if (contents.extended & CFLAGS_DETAIL_ILLUSIONARY) {
return 6;
} else if (contents.extended & CFLAGS_DETAIL_FENCE) {
return 7;
} else if (contents.extended & CFLAGS_ILLUSIONARY_VISBLOCKER) {
return 2;
} else switch( contents.native & (Q2_LAST_VISIBLE_CONTENTS - 1) ) {
case Q2_CONTENTS_SOLID: return 10;
case Q2_CONTENTS_WINDOW: return 9;
case Q2_CONTENTS_AUX: return 5;
case Q2_CONTENTS_LAVA: return 4;
case Q2_CONTENTS_SLIME: return 3;
case Q2_CONTENTS_WATER: return 2;
case Q2_CONTENTS_MIST: return 1;
default: return 0;
}
}
contentflags_t create_empty_contents(const int32_t &cflags) const {
return { 0, cflags };
}
contentflags_t create_solid_contents(const int32_t &cflags) const {
return { Q2_CONTENTS_SOLID, cflags };
}
contentflags_t create_sky_contents(const int32_t &cflags) const {
return create_solid_contents(cflags);
}
contentflags_t create_liquid_contents(const int32_t &liquid_type, const int32_t &cflags) const {
switch (liquid_type) {
case CONTENTS_WATER:
return { Q2_CONTENTS_WATER, cflags };
case CONTENTS_SLIME:
return { Q2_CONTENTS_SLIME, cflags };
case CONTENTS_LAVA:
return { Q2_CONTENTS_LAVA, cflags };
default:
Error("bad contents");
}
}
bool contents_are_sky(const contentflags_t &contents) const {
return false;
}
bool contents_are_liquid(const contentflags_t &contents) const {
return contents.native & Q2_CONTENTS_LIQUID;
}
bool contents_are_valid(const contentflags_t &contents, bool strict) const {
if (!strict) {
return true;
}
return contents.native & (Q2_CONTENTS_SOLID | Q2_CONTENTS_WINDOW | Q2_CONTENTS_LIQUID | Q2_CONTENTS_MIST | Q2_CONTENTS_AUX);
}
};
static const gamedef_generic_t gamedef_generic;
@ -141,6 +295,35 @@ static const gamedef_q2_t gamedef_q2;
const bspversion_t bspver_q2 { Q2_BSPIDENT, Q2_BSPVERSION, "q2bsp", "Quake II BSP", &gamedef_q2 };
const bspversion_t bspver_qbism { Q2_QBISMIDENT, Q2_BSPVERSION, "qbism", "Quake II Qbism BSP", &gamedef_q2 };
bool contentflags_t::types_equal(const contentflags_t &other, const gamedef_t *game) const {
return (extended & CFLAGS_DETAIL_MASK) == (other.extended & CFLAGS_DETAIL_MASK) &&
game->get_content_type(*this) == game->get_content_type(other);
}
int32_t contentflags_t::priority(const gamedef_t *game) const {
return game->contents_priority(*this);
}
bool contentflags_t::is_empty(const gamedef_t *game) const {
return game->contents_are_empty(*this);
}
bool contentflags_t::is_solid(const gamedef_t *game) const {
return game->contents_are_solid(*this);
}
bool contentflags_t::is_sky(const gamedef_t *game) const {
return game->contents_are_sky(*this);
}
bool contentflags_t::is_liquid(const gamedef_t *game) const {
return game->contents_are_liquid(*this);
}
bool contentflags_t::is_valid(const gamedef_t *game, bool strict) const {
return game->contents_are_valid(*this, strict);
}
static const char *
BSPVersionString(const bspversion_t *version)
{

View File

@ -263,40 +263,87 @@ typedef struct {
#define Q2_CONTENTS_LADDER 0x20000000
// Special contents flags for the compiler only
#define CFLAGS_STRUCTURAL_COVERED_BY_DETAIL (1U << 0)
#define CFLAGS_WAS_ILLUSIONARY (1U << 1) /* was illusionary, got changed to something else */
#define CFLAGS_DETAIL_WALL (1U << 2) /* don't clip world for func_detail_wall entities */
#define CFLAGS_BMODEL_MIRROR_INSIDE (1U << 3) /* set "_mirrorinside" "1" on a bmodel to mirror faces for when the player is inside. */
#define CFLAGS_NO_CLIPPING_SAME_TYPE (1U << 4) /* Don't clip the same content type. mostly intended for CONTENTS_DETAIL_ILLUSIONARY */
#define CFLAGS_STRUCTURAL_COVERED_BY_DETAIL (1 << 0)
#define CFLAGS_WAS_ILLUSIONARY (1 << 1) /* was illusionary, got changed to something else */
#define CFLAGS_DETAIL_WALL (1 << 2) /* don't clip world for func_detail_wall entities */
#define CFLAGS_BMODEL_MIRROR_INSIDE (1 << 3) /* set "_mirrorinside" "1" on a bmodel to mirror faces for when the player is inside. */
#define CFLAGS_NO_CLIPPING_SAME_TYPE (1 << 4) /* Don't clip the same content type. mostly intended for CONTENTS_DETAIL_ILLUSIONARY */
// only one of these flags below should ever be set.
#define CFLAGS_HINT (1U << 5)
#define CFLAGS_CLIP (1U << 6)
#define CFLAGS_ORIGIN (1U << 7)
#define CFLAGS_DETAIL (1U << 8)
#define CFLAGS_DETAIL_ILLUSIONARY (1U << 9)
#define CFLAGS_DETAIL_FENCE (1U << 10)
#define CFLAGS_ILLUSIONARY_VISBLOCKER (1U << 11)
#define CFLAGS_HINT (1 << 5)
#define CFLAGS_CLIP (1 << 6)
#define CFLAGS_ORIGIN (1 << 7)
#define CFLAGS_DETAIL (1 << 8)
#define CFLAGS_DETAIL_ILLUSIONARY (1 << 9)
#define CFLAGS_DETAIL_FENCE (1 << 10)
#define CFLAGS_ILLUSIONARY_VISBLOCKER (1 << 11)
// all of the detail values
#define CFLAGS_DETAIL_MASK (CFLAGS_DETAIL | CFLAGS_DETAIL_ILLUSIONARY | CFLAGS_DETAIL_FENCE)
struct gamedef_t;
struct contentflags_t {
// native flags value; what's written to the BSP basically
int32_t native;
// extra flags, specific to BSP only
uint16_t extended;
int32_t extended;
// merge these content flags with other, and use
// their native contents.
contentflags_t merge(const contentflags_t &other) const {
return { other.native, (uint16_t) (extended | other.extended) };
constexpr contentflags_t merge(const contentflags_t &other) const {
return { other.native, extended | other.extended };
}
bool operator==(const contentflags_t &other) const {
constexpr bool operator==(const contentflags_t &other) const {
return native == other.native && extended == other.extended;
}
bool operator!=(const contentflags_t &other) const {
constexpr bool operator!=(const contentflags_t &other) const {
return !(*this == other);
}
// check if these contents are marked as any (or a specific kind of) detail brush.
constexpr bool is_detail(int32_t types = CFLAGS_DETAIL_MASK) const {
return (extended & CFLAGS_DETAIL_MASK) & types;
}
bool is_empty(const gamedef_t *game) const;
bool is_solid(const gamedef_t *game) const;
bool is_sky(const gamedef_t *game) const;
bool is_liquid(const gamedef_t *game) const;
bool is_valid(const gamedef_t *game, bool strict = true) const;
bool is_structural_solid(const gamedef_t *game) const {
return is_solid(game) && !is_detail();
}
bool is_structural_sky(const gamedef_t *game) const {
return is_sky(game) && !is_detail();
}
bool is_structural_sky_or_solid(const gamedef_t *game) const {
return (is_sky(game) || is_solid(game)) && !is_detail();
}
constexpr bool is_hint() const {
return extended & CFLAGS_HINT;
}
constexpr bool clips_same_type() const {
return !(extended & CFLAGS_NO_CLIPPING_SAME_TYPE);
}
constexpr bool is_fence() const {
return is_detail(CFLAGS_DETAIL_FENCE | CFLAGS_DETAIL_ILLUSIONARY);
}
// check if this content's `type` - which is distinct from various
// flags that turn things on/off - match. Exactly what the native
// "type" is depends on the game, but any of the detail flags must
// also match.
bool types_equal(const contentflags_t &other, const gamedef_t *game) const;
int32_t priority(const gamedef_t *game) const;
};
struct bsp29_dnode_t {
@ -1042,6 +1089,23 @@ struct gamedef_t
virtual bool surf_is_lightmapped(const surfflags_t &flags) const = 0;
virtual bool surf_is_subdivided(const surfflags_t &flags) const = 0;
virtual contentflags_t cluster_contents(const contentflags_t &contents0, const contentflags_t &contents1) const = 0;
virtual int32_t get_content_type(const contentflags_t &contents) const = 0;
virtual int32_t contents_priority(const contentflags_t &contents) const = 0;
virtual contentflags_t create_empty_contents(const int32_t &cflags = 0) const = 0;
virtual contentflags_t create_solid_contents(const int32_t &cflags = 0) const = 0;
virtual contentflags_t create_sky_contents(const int32_t &cflags = 0) const = 0;
virtual contentflags_t create_liquid_contents(const int32_t &liquid_type, const int32_t &cflags = 0) const = 0;
virtual bool contents_are_empty(const contentflags_t &contents) const {
return contents.native == create_empty_contents().native;
}
virtual bool contents_are_solid(const contentflags_t &contents) const {
return contents.native == create_solid_contents().native;
}
virtual bool contents_are_sky(const contentflags_t &contents) const {
return contents.native == create_sky_contents().native;
}
virtual bool contents_are_liquid(const contentflags_t &contents) const = 0;
virtual bool contents_are_valid(const contentflags_t &contents, bool strict = true) const = 0;
};
// BSP version struct & instances

View File

@ -220,10 +220,7 @@ typedef struct node_s {
mapentity_t *occupant; // example occupant, for leak hunting
bool detail_separator; // for vis portal generation. true if ALL faces on node, and on all descendant nodes/leafs, are detail.
bool opaque() const {
return contents.native == CONTENTS_SOLID
|| contents.native == CONTENTS_SKY;
}
bool opaque() const;
} node_t;
#include <qbsp/brush.hh>

View File

@ -27,7 +27,6 @@
extern std::atomic<int> splitnodes;
void DetailToSolid(node_t *node);
int Contents_Priority(const contentflags_t &contents);
const char *GetContentsName( const contentflags_t &Contents );
void DivideFacet(face_t *in, qbsp_plane_t *split, face_t **front, face_t **back);
void CalcSurfaceInfo(surface_t *surf);

View File

@ -356,6 +356,17 @@ FixRotateOrigin(mapentity_t *entity)
SetKeyValue(entity, "origin", value);
}
static bool
DiscardHintSkipFace_Q1(const int hullnum, const hullbrush_t *hullbrush, const mtexinfo_t &texinfo) {
const char *texname = map.miptexTextureName(texinfo.miptex).c_str();
return Q_strcasecmp(texname, "hint"); // anything texname other than "hint" in a hint brush is treated as "hintskip", and discarded
}
static bool
DiscardHintSkipFace_Q2(const int hullnum, const hullbrush_t *hullbrush, const mtexinfo_t &texinfo) {
return texinfo.flags.native & Q2_SURF_SKIP; // skip brushes in a hint brush are treated as "hintskip", and discarded
}
/*
=================
@ -383,15 +394,16 @@ CreateBrushFaces(const mapentity_t *src, hullbrush_t *hullbrush,
hullbrush->maxs[i] = -VECT_MAX;
}
auto DiscardHintSkipFace = (options.target_version->game->id == GAME_QUAKE_II) ? DiscardHintSkipFace_Q2 : DiscardHintSkipFace_Q1;
mapface = hullbrush->faces;
for (i = 0; i < hullbrush->numfaces; i++, mapface++) {
if (!hullnum && (hullbrush->contents.extended & CFLAGS_HINT)) {
if (!hullnum && hullbrush->contents.is_hint()) {
/* Don't generate hintskip faces */
const mtexinfo_t &texinfo = map.mtexinfos.at(mapface->texinfo);
const char *texname = map.miptexTextureName(texinfo.miptex).c_str();
if (Q_strcasecmp(texname, "hint"))
continue; // anything texname other than "hint" in a hint brush is treated as "hintskip", and discarded
if (DiscardHintSkipFace(hullnum, hullbrush, texinfo))
continue;
}
w = BaseWindingForPlane(&mapface->plane);
@ -832,11 +844,36 @@ Brush_IsDetail(const mapbrush_t *mapbrush)
return false;
}
// adjust the given content flags from the texture name input.
// this is where special names are transformed into game-specific
// contents.
static bool AdjustContentsFromName(const char *texname, contentflags_t &flags) {
if (!Q_strcasecmp(texname, "origin"))
flags = flags.merge(options.target_version->game->create_empty_contents(CFLAGS_ORIGIN));
else if (!Q_strcasecmp(texname, "hint"))
flags = flags.merge(options.target_version->game->create_empty_contents(CFLAGS_HINT));
else if (!Q_strcasecmp(texname, "clip"))
flags = flags.merge(options.target_version->game->create_solid_contents(CFLAGS_CLIP));
else if (texname[0] == '*') {
if (!Q_strncasecmp(texname + 1, "lava", 4))
flags = flags.merge(options.target_version->game->create_liquid_contents(CONTENTS_LAVA));
else if (!Q_strncasecmp(texname + 1, "slime", 5))
flags = flags.merge(options.target_version->game->create_liquid_contents(CONTENTS_SLIME));
flags = flags.merge(options.target_version->game->create_liquid_contents(CONTENTS_WATER));
}
else if (!Q_strncasecmp(texname, "sky", 3))
flags = flags.merge(options.target_version->game->create_sky_contents());
else
return false;
return true;
}
static contentflags_t
Brush_GetContents(const mapbrush_t *mapbrush)
Brush_GetContents_Q1(const mapbrush_t *mapbrush, const contentflags_t &base_contents)
{
const char *texname;
contentflags_t contents = base_contents;
//check for strong content indicators
for (int i = 0; i < mapbrush->numfaces; i++)
@ -845,36 +882,65 @@ Brush_GetContents(const mapbrush_t *mapbrush)
const mtexinfo_t &texinfo = map.mtexinfos.at(mapface.texinfo);
texname = map.miptexTextureName(texinfo.miptex).c_str();
// if we were already assigned contents from somewhere else
// (Quake II), use this.
if (options.target_version->game->id == GAME_QUAKE_II) {
if (mapface.contents) {
return { mapface.contents };
}
continue;
if (AdjustContentsFromName(texname, contents)) {
return contents;
}
if (!Q_strcasecmp(texname, "origin"))
return { CONTENTS_EMPTY, CFLAGS_ORIGIN };
if (!Q_strcasecmp(texname, "hint"))
return { CONTENTS_EMPTY, CFLAGS_HINT };
if (!Q_strcasecmp(texname, "clip"))
return { CONTENTS_SOLID, CFLAGS_CLIP };
if (texname[0] == '*') {
if (!Q_strncasecmp(texname + 1, "lava", 4))
return { CONTENTS_LAVA };
if (!Q_strncasecmp(texname + 1, "slime", 5))
return { CONTENTS_SLIME };
return { CONTENTS_WATER };
}
if (!Q_strncasecmp(texname, "sky", 3))
return { CONTENTS_SKY };
}
//and anything else is assumed to be a regular solid.
return { options.target_version->game->id == GAME_QUAKE_II ? Q2_CONTENTS_SOLID : CONTENTS_SOLID };
return options.target_version->game->create_solid_contents();
}
static contentflags_t
Brush_GetContents_Q2 (const mapbrush_t *mapbrush, const contentflags_t &base_contents)
{
bool is_trans = false;
bool is_hint = false;
contentflags_t contents = base_contents.merge({ mapbrush->face(0).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);
if (!is_trans && (texinfo.flags.native & (Q2_SURF_TRANS33 | Q2_SURF_TRANS66))) {
is_trans = true;
}
if (!is_hint && (texinfo.flags.native & Q2_SURF_HINT)) {
is_hint = true;
}
if (mapface.contents != contents.native) {
logprint("mixed face contents\n"); // TODO: need entity # and brush #
break;
}
}
// if any side is translucent, mark the contents
// and change solid to window
if (is_trans) {
contents.native = (contents.native & ~Q2_CONTENTS_SOLID) | (Q2_CONTENTS_TRANSLUCENT | 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 (is_hint) {
contents.extended |= CFLAGS_HINT;
}
return contents;
}
@ -1114,7 +1180,6 @@ Brush_LoadEntity(mapentity_t *dst, const mapentity_t *src, const int hullnum)
int i;
int lmshift;
bool all_detail, all_detail_fence, all_detail_illusionary;
contentflags_t contents { };
/*
* The brush list needs to be ordered (lowest to highest priority):
@ -1132,9 +1197,21 @@ Brush_LoadEntity(mapentity_t *dst, const mapentity_t *src, const int hullnum)
rotation_t rottype = rotation_t::none;
VectorCopy(vec3_origin, rotate_offset);
const bool func_illusionary_visblocker =
(0 == Q_strcasecmp(classname, "func_illusionary_visblocker"));
contentflags_t base_contents = options.target_version->game->create_empty_contents();
if (func_illusionary_visblocker) {
base_contents.extended |= CFLAGS_ILLUSIONARY_VISBLOCKER;
}
// TODO: move to game
auto Brush_GetContents = (options.target_version->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);
const contentflags_t contents = Brush_GetContents(mapbrush, base_contents);
if (contents.extended & CFLAGS_ORIGIN) {
if (dst == pWorldEnt()) {
Message(msgWarning, warnOriginBrushInWorld);
@ -1175,7 +1252,7 @@ Brush_LoadEntity(mapentity_t *dst, const mapentity_t *src, const int hullnum)
}
if (!Q_strcasecmp(classname, "func_detail_wall") && !options.fNodetail) {
all_detail = true;
contents.extended |= CFLAGS_DETAIL_WALL;
base_contents.extended |= CFLAGS_DETAIL_WALL;
}
all_detail_fence = false;
@ -1200,20 +1277,17 @@ Brush_LoadEntity(mapentity_t *dst, const mapentity_t *src, const int hullnum)
/* _mirrorinside key (for func_water etc.) */
if (atoi(ValueForKey(src, "_mirrorinside"))) {
contents.extended |= CFLAGS_BMODEL_MIRROR_INSIDE;
base_contents.extended |= CFLAGS_BMODEL_MIRROR_INSIDE;
}
/* _noclipfaces */
if (atoi(ValueForKey(src, "_noclipfaces"))) {
contents.extended |= CFLAGS_NO_CLIPPING_SAME_TYPE;
base_contents.extended |= CFLAGS_NO_CLIPPING_SAME_TYPE;
}
const bool func_illusionary_visblocker =
(0 == Q_strcasecmp(classname, "func_illusionary_visblocker"));
for (i = 0; i < src->nummapbrushes; i++, mapbrush++) {
mapbrush = &src->mapbrush(i);
contents = contents.merge(Brush_GetContents(mapbrush));
contentflags_t contents = Brush_GetContents(mapbrush, base_contents);
// per-brush settings
bool detail = Brush_IsDetail(mapbrush);
@ -1224,21 +1298,15 @@ Brush_LoadEntity(mapentity_t *dst, const mapentity_t *src, const int hullnum)
detail |= all_detail;
detail_illusionary |= all_detail_illusionary;
detail_fence |= all_detail_fence;
/* FIXME: move into Brush_GetContents? */
if (func_illusionary_visblocker) {
contents.native = CONTENTS_EMPTY;
contents.extended |= CFLAGS_ILLUSIONARY_VISBLOCKER;
}
/* "origin" brushes always discarded */
if (contents.extended & CFLAGS_ORIGIN)
continue;
/* -omitdetail option omits all types of detail */
if (options.fOmitDetail && detail && !(contents.extended & CFLAGS_DETAIL_WALL))
if (options.fOmitDetail && detail && !contents.is_detail(CFLAGS_DETAIL_WALL))
continue;
if ((options.fOmitDetail || options.fOmitDetailWall) && detail && (contents.extended & CFLAGS_DETAIL_WALL))
if ((options.fOmitDetail || options.fOmitDetailWall) && detail && contents.is_detail(CFLAGS_DETAIL_WALL))
continue;
if ((options.fOmitDetail || options.fOmitDetailIllusionary) && detail_illusionary)
continue;
@ -1246,15 +1314,13 @@ Brush_LoadEntity(mapentity_t *dst, const mapentity_t *src, const int hullnum)
continue;
/* turn solid brushes into detail, if we're in hull0 */
if (hullnum == 0 && contents.native == CONTENTS_SOLID) {
if (hullnum == 0 && contents.is_solid(options.target_version->game)) {
if (detail) {
contents.extended |= CFLAGS_DETAIL;
} else if (detail_illusionary) {
contents.native = CONTENTS_EMPTY;
contents.extended |= CFLAGS_DETAIL_ILLUSIONARY;
contents = contents.merge(options.target_version->game->create_empty_contents(CFLAGS_DETAIL_ILLUSIONARY));
} else if (detail_fence) {
contents.native = CONTENTS_EMPTY; // fences need to generate leaves
contents.extended |= CFLAGS_DETAIL_FENCE;
contents = contents.merge(options.target_version->game->create_empty_contents(CFLAGS_DETAIL_FENCE)); // fences need to generate leaves
}
}
@ -1282,15 +1348,15 @@ Brush_LoadEntity(mapentity_t *dst, const mapentity_t *src, const int hullnum)
}
/* "hint" brushes don't affect the collision hulls */
if (contents.extended & CFLAGS_HINT) {
if (contents.is_hint()) {
if (hullnum)
continue;
contents.native = CONTENTS_EMPTY;
contents = contents.merge(options.target_version->game->create_empty_contents());
}
/* entities never use water merging */
if (dst != pWorldEnt())
contents.native = CONTENTS_SOLID;
contents = contents.merge(options.target_version->game->create_solid_contents());
/* Hack to turn bmodels with "_mirrorinside" into func_detail_fence in hull 0.
this is to allow "_mirrorinside" to work on func_illusionary, func_wall, etc.
@ -1301,20 +1367,19 @@ Brush_LoadEntity(mapentity_t *dst, const mapentity_t *src, const int hullnum)
contents type.
*/
if (dst != pWorldEnt() && hullnum == 0 && (contents.extended & CFLAGS_BMODEL_MIRROR_INSIDE)) {
contents.native = CONTENTS_EMPTY;
contents.extended |= CFLAGS_DETAIL_FENCE;
contents = contents.merge(options.target_version->game->create_empty_contents(CFLAGS_DETAIL_FENCE));
}
/* nonsolid brushes don't show up in clipping hulls */
// TODO: will this statement need to be modified since clip
// detail etc aren't native types any more?
if (hullnum > 0 && contents.native != CONTENTS_SOLID && contents.native != CONTENTS_SKY)
if (hullnum > 0 && !contents.is_solid(options.target_version->game) && !contents.is_sky(options.target_version->game))
continue;
/* sky brushes are solid in the collision hulls */
if (hullnum > 0 && contents.native == CONTENTS_SKY)
contents.native = CONTENTS_SOLID;
if (hullnum > 0 && contents.is_sky(options.target_version->game))
contents = contents.merge(options.target_version->game->create_solid_contents());
brush_t *brush = LoadBrush(src, mapbrush, contents, rotate_offset, rottype, hullnum);
if (!brush)
continue;
@ -1322,19 +1387,19 @@ Brush_LoadEntity(mapentity_t *dst, const mapentity_t *src, const int hullnum)
dst->numbrushes++;
brush->lmshift = lmshift;
if (brush->contents.extended & CFLAGS_DETAIL) {
if (brush->contents.is_detail(CFLAGS_DETAIL)) {
brush->next = dst->detail;
dst->detail = brush;
} else if (brush->contents.extended & CFLAGS_DETAIL_ILLUSIONARY) {
} else if (brush->contents.is_detail(CFLAGS_DETAIL_ILLUSIONARY)) {
brush->next = dst->detail_illusionary;
dst->detail_illusionary = brush;
} else if (brush->contents.extended & CFLAGS_DETAIL_FENCE) {
} else if (brush->contents.is_detail(CFLAGS_DETAIL_FENCE)) {
brush->next = dst->detail_fence;
dst->detail_fence = brush;
} else if (brush->contents.native == CONTENTS_SOLID) {
} else if (brush->contents.is_solid(options.target_version->game)) {
brush->next = dst->solid;
dst->solid = brush;
} else if (brush->contents.native == CONTENTS_SKY) {
} else if (brush->contents.is_sky(options.target_version->game)) {
brush->next = dst->sky;
dst->sky = brush;
} else {

View File

@ -373,8 +373,8 @@ SaveFacesToPlaneList(face_t *facelist, bool mirror, std::map<int, face_t *> &pla
// HACK: We only want this mirrored face for CONTENTS_DETAIL
// to force the right content type for the leaf, but we don't actually
// want the face. So just set the texinfo to "skip" so it gets deleted.
if ((face->contents[1].extended & (CFLAGS_DETAIL | CFLAGS_DETAIL_ILLUSIONARY | CFLAGS_DETAIL_FENCE | CFLAGS_WAS_ILLUSIONARY))
|| (options.fContentHack && face->contents[1].native == CONTENTS_SOLID)) {
if ((face->contents[1].is_detail() || (face->contents[1].extended & CFLAGS_WAS_ILLUSIONARY))
|| (options.fContentHack && face->contents[1].is_structural_solid(options.target_version->game))) {
// if CFLAGS_BMODEL_MIRROR_INSIDE is set, never change to skip
if (!(face->contents[1].extended & CFLAGS_BMODEL_MIRROR_INSIDE)) {
@ -423,7 +423,7 @@ contents override the face inside contents.
static void
SaveInsideFaces(face_t *face, const brush_t *clipbrush, face_t **savelist)
{
Q_assert(clipbrush->contents.native != CONTENTS_SOLID);
Q_assert(!clipbrush->contents.is_solid(options.target_version->game));
face_t *next;
@ -434,8 +434,8 @@ SaveInsideFaces(face_t *face, const brush_t *clipbrush, face_t **savelist)
next = face->next;
face->contents[0] = clipbrush->contents;
if ((face->contents[1].native == CONTENTS_SOLID || face->contents[1].native == CONTENTS_SKY)
&& (clipbrush->contents.extended & CFLAGS_DETAIL)) {
if ((face->contents[1].is_solid(options.target_version->game) || face->contents[1].is_sky(options.target_version->game))
&& clipbrush->contents.is_detail(CFLAGS_DETAIL)) {
// This case is when a structural and detail brush are touching,
// and we want to save the sturctural face that is
// touching detail.
@ -450,26 +450,26 @@ SaveInsideFaces(face_t *face, const brush_t *clipbrush, face_t **savelist)
// marked as empty here, and the detail faces have their "back"
// marked as detail.
face->contents[0] = { CONTENTS_EMPTY, CFLAGS_STRUCTURAL_COVERED_BY_DETAIL };
face->contents[0] = options.target_version->game->create_empty_contents(CFLAGS_STRUCTURAL_COVERED_BY_DETAIL);
face->texinfo = MakeSkipTexinfo();
}
// N.B.: We don't need a hack like above for when clipbrush->contents == CONTENTS_DETAIL_ILLUSIONARY.
// These would create leaks
Q_assert(!((face->contents[1].native == CONTENTS_SKY || face->contents[1].native == CONTENTS_SOLID) &&
(face->contents[0].extended & CFLAGS_DETAIL)));
Q_assert(!((face->contents[1].is_sky(options.target_version->game) || face->contents[1].is_solid(options.target_version->game)) &&
face->contents[0].is_detail(CFLAGS_DETAIL)));
/*
* If the inside brush is empty space, inherit the outside contents.
* The only brushes with empty contents currently are hint brushes.
*/
if (face->contents[1].extended & CFLAGS_DETAIL_ILLUSIONARY) {
face->contents[1] = { clipbrush->contents.native, (uint16_t) ((face->contents[1].extended & ~CFLAGS_DETAIL_ILLUSIONARY) | CFLAGS_WAS_ILLUSIONARY) };
if (face->contents[1].is_detail(CFLAGS_DETAIL_ILLUSIONARY)) {
face->contents[1] = { clipbrush->contents.native, (face->contents[1].extended & ~CFLAGS_DETAIL_ILLUSIONARY) | CFLAGS_WAS_ILLUSIONARY };
}
if (face->contents[1].native == CONTENTS_EMPTY)
if (face->contents[1].is_empty(options.target_version->game)) {
face->contents[1] = clipbrush->contents;
}
face->next = *savelist;
*savelist = face;
@ -533,7 +533,7 @@ CopyBrushFaces(const brush_t *brush)
brushfaces++;
newface = (face_t *)AllocMem(OTHER, sizeof(face_t), true);
*newface = *face;
newface->contents[0] = { CONTENTS_EMPTY };
newface->contents[0] = options.target_version->game->create_empty_contents();
newface->contents[1] = brush->contents;
newface->lmshift[0] = brush->lmshift;
newface->lmshift[1] = brush->lmshift;
@ -544,17 +544,6 @@ CopyBrushFaces(const brush_t *brush)
return facelist;
}
static bool IsLiquid(const contentflags_t &contents)
{
return contents.native == CONTENTS_WATER
|| contents.native == CONTENTS_LAVA
|| contents.native == CONTENTS_SLIME;
}
static bool IsFence(const contentflags_t &contents) {
return contents.extended & (CFLAGS_DETAIL_FENCE | CFLAGS_DETAIL_ILLUSIONARY);
}
/*
==================
CSGFaces
@ -610,35 +599,35 @@ CSGFaces(const mapentity_t *entity)
overwrite = true;
continue;
}
if (clipbrush->contents.native == CONTENTS_EMPTY) {
if (clipbrush->contents.is_hint()) {
/* Ensure hint never clips anything */
continue;
}
if ((clipbrush->contents.extended & CFLAGS_DETAIL_ILLUSIONARY)
&& !(brush->contents.extended & CFLAGS_DETAIL_ILLUSIONARY)) {
if (clipbrush->contents.is_detail(CFLAGS_DETAIL_ILLUSIONARY)
&& !brush->contents.is_detail(CFLAGS_DETAIL_ILLUSIONARY)) {
/* CONTENTS_DETAIL_ILLUSIONARY never clips anything but itself */
continue;
}
if ((clipbrush->contents.extended & CFLAGS_DETAIL) && (clipbrush->contents.extended & CFLAGS_DETAIL_WALL)
&& !((brush->contents.extended & CFLAGS_DETAIL) && (brush->contents.extended & CFLAGS_DETAIL_WALL))) {
if (clipbrush->contents.is_detail(CFLAGS_DETAIL) && clipbrush->contents.is_detail(CFLAGS_DETAIL_WALL)
&& !(brush->contents.is_detail(CFLAGS_DETAIL) && brush->contents.is_detail(CFLAGS_DETAIL_WALL))) {
/* if clipbrush has CONTENTS_DETAIL and CFLAGS_DETAIL_WALL are set,
only clip other brushes with both CONTENTS_DETAIL and CFLAGS_DETAIL_WALL.
*/
continue;
}
if ((clipbrush->contents.extended & CFLAGS_DETAIL_FENCE)
&& !(brush->contents.extended & CFLAGS_DETAIL_FENCE)) {
if (clipbrush->contents.is_detail(CFLAGS_DETAIL_FENCE)
&& !brush->contents.is_detail(CFLAGS_DETAIL_FENCE)) {
/* CONTENTS_DETAIL_FENCE never clips anything but itself */
continue;
}
// TODO: this might break because this == won't catch the extended types now.
// might need a specific function for this one.
if (clipbrush->contents.native == brush->contents.native
&& (clipbrush->contents.extended & CFLAGS_NO_CLIPPING_SAME_TYPE)) {
if (clipbrush->contents.types_equal(brush->contents, options.target_version->game)
&& !clipbrush->contents.clips_same_type()) {
/* _noclipfaces key */
continue;
}
@ -679,14 +668,18 @@ CSGFaces(const mapentity_t *entity)
*
* FIXME: clean this up, the predicate seems to be "can you see 'brush' from inside 'clipbrush'"
*/
if ((brush->contents.native == CONTENTS_SOLID && clipbrush->contents.native != CONTENTS_SOLID)
|| (brush->contents.native == CONTENTS_SKY && (clipbrush->contents.native != CONTENTS_SOLID
&& clipbrush->contents.native != CONTENTS_SKY))
|| ((brush->contents.extended & CFLAGS_DETAIL) && (clipbrush->contents.native != CONTENTS_SOLID
&& clipbrush->contents.native != CONTENTS_SKY
&& !(clipbrush->contents.extended & CFLAGS_DETAIL)))
|| (IsLiquid(brush->contents) && (clipbrush->contents.extended & CFLAGS_DETAIL_ILLUSIONARY))
|| (IsFence(brush->contents) && (IsLiquid(clipbrush->contents) || IsFence(clipbrush->contents))))
if ((brush->contents.is_structural_solid(options.target_version->game) && !clipbrush->contents.is_structural_solid(options.target_version->game))
|| (brush->contents.is_structural_sky(options.target_version->game) && !clipbrush->contents.is_structural_sky_or_solid(options.target_version->game))
|| ((brush->contents.is_solid(options.target_version->game) && brush->contents.is_detail(CFLAGS_DETAIL)) &&
(!clipbrush->contents.is_solid(options.target_version->game)
&& !clipbrush->contents.is_sky(options.target_version->game)
&& !clipbrush->contents.is_detail(CFLAGS_DETAIL)))
|| (brush->contents.is_liquid(options.target_version->game) && clipbrush->contents.is_detail(CFLAGS_DETAIL_ILLUSIONARY))
|| (brush->contents.is_fence() && (clipbrush->contents.is_liquid(options.target_version->game) || clipbrush->contents.is_fence())))
{
SaveInsideFaces(inside, clipbrush, &outside);
} else {
@ -708,7 +701,7 @@ CSGFaces(const mapentity_t *entity)
* All of the faces left on the outside list are real surface faces
* If the brush is non-solid, mirror faces for the inside view
*/
const bool mirror = options.fContentHack ? true : (brush->contents.native != CONTENTS_SOLID);
const bool mirror = options.fContentHack ? true : !brush->contents.is_solid(options.target_version->game);
SaveFacesToPlaneList(outside, mirror, planefaces);
}
surface_t *surfaces = BuildSurfaces(planefaces);

View File

@ -338,7 +338,7 @@ ClearOutFaces(node_t *node)
}
// visit the leaf
if (node->contents.native != CONTENTS_SOLID) {
if (!node->contents.is_solid(options.target_version->game)) {
return;
}
@ -365,8 +365,8 @@ OutLeafsToSolid_r(node_t *node, int *outleafs_count)
return;
// Don't fill sky, or count solids as outleafs
if (node->contents.native == CONTENTS_SKY
|| node->contents.native == CONTENTS_SOLID)
if (node->contents.is_sky(options.target_version->game)
|| node->contents.is_solid(options.target_version->game))
return;
// Now check all faces touching the leaf. If any of them are partially going into the occupied part of the map,
@ -383,7 +383,7 @@ OutLeafsToSolid_r(node_t *node, int *outleafs_count)
}
// Finally, we can fill it in as void.
node->contents = { CONTENTS_SOLID };
node->contents = options.target_version->game->create_solid_contents();
*outleafs_count += 1;
}

View File

@ -72,7 +72,7 @@ PortalThru(const portal_t *p)
contentflags_t contents1 = ClusterContents(p->nodes[1]);
/* Can't see through solids */
if (contents0.native == CONTENTS_SOLID || contents1.native == CONTENTS_SOLID)
if (contents0.is_structural_solid(options.target_version->game) || contents1.is_structural_solid(options.target_version->game))
return false;
/* Can't see through func_illusionary_visblocker */
@ -85,19 +85,17 @@ PortalThru(const portal_t *p)
/* If water is transparent, liquids are like empty space */
if (options.fTranswater) {
if (contents0.native >= CONTENTS_LAVA && contents0.native <= CONTENTS_WATER &&
contents1.native == CONTENTS_EMPTY)
if (contents0.is_liquid(options.target_version->game) && contents1.is_empty(options.target_version->game))
return true;
if (contents1.native >= CONTENTS_LAVA && contents1.native <= CONTENTS_WATER &&
contents0.native == CONTENTS_EMPTY)
if (contents1.is_liquid(options.target_version->game) && contents0.is_empty(options.target_version->game))
return true;
}
/* If sky is transparent, then sky is like empty space */
if (options.fTranssky) {
if (contents0.native == CONTENTS_SKY && contents1.native == CONTENTS_EMPTY)
if (contents0.is_sky(options.target_version->game) && contents1.is_empty(options.target_version->game))
return true;
if (contents0.native == CONTENTS_EMPTY && contents1.native == CONTENTS_SKY)
if (contents0.is_empty(options.target_version->game) && contents1.is_sky(options.target_version->game))
return true;
}
@ -118,7 +116,7 @@ WritePortals_r(node_t *node, FILE *portalFile, bool clusters)
WritePortals_r(node->children[1], portalFile, clusters);
return;
}
if (node->contents.native == CONTENTS_SOLID)
if (node->contents.is_solid(options.target_version->game))
return;
for (p = node->portals; p; p = next) {
@ -163,7 +161,7 @@ WriteClusters_r(node_t *node, FILE *portalFile, int viscluster)
viscluster = WriteClusters_r(node->children[1], portalFile, viscluster);
return viscluster;
}
if (node->contents.native == CONTENTS_SOLID)
if (node->contents.is_solid(options.target_version->game))
return viscluster;
/* If we're in the next cluster, start a new line */
@ -225,7 +223,7 @@ NumberLeafs_r(node_t *node, portal_state_t *state, int cluster)
return;
}
if (node->contents.native == CONTENTS_SOLID) {
if (node->contents.is_solid(options.target_version->game)) {
/* solid block, viewpoint never inside */
node->visleafnum = -1;
node->viscluster = -1;
@ -381,7 +379,7 @@ MakeHeadnodePortals(const mapentity_t *entity, node_t *node)
bounds[1][i] = entity->maxs[i] + SIDESPACE;
}
outside_node.contents = { CONTENTS_SOLID };
outside_node.contents = options.target_version->game->create_solid_contents();
outside_node.portals = NULL;
for (i = 0; i < 3; i++)

View File

@ -34,6 +34,10 @@ static const char *IntroString =
// command line flags
options_t options;
bool node_t::opaque() const {
return contents.is_structural_sky_or_solid(options.target_version->game);
}
/*
===============
ProcessEntity

View File

@ -74,10 +74,10 @@ DetailToSolid(node_t *node)
// If both children are solid, we can merge the two leafs into one.
// DarkPlaces has an assertion that fails if both children are
// solid.
if (node->children[0]->contents.native == CONTENTS_SOLID
&& node->children[1]->contents.native == CONTENTS_SOLID) {
if (node->children[0]->contents.is_structural_solid(options.target_version->game)
&& node->children[1]->contents.is_structural_solid(options.target_version->game)) {
// This discards any faces on-node. Should be safe (?)
ConvertNodeToLeaf(node, { CONTENTS_SOLID });
ConvertNodeToLeaf(node, options.target_version->game->create_solid_contents());
}
}
}
@ -538,7 +538,7 @@ CalcSurfaceInfo(surface_t *surf)
surf->has_struct = false;
for (const face_t *f = surf->faces; f; f = f->next) {
if (f->contents[0].native >= 0 || f->contents[1].native >= 0)
if (!f->contents[0].is_valid(options.target_version->game, false) || !f->contents[1].is_valid(options.target_version->game, false))
Error("Bad contents in face (%s)", __func__);
surf->lmshift = (f->lmshift[0]<f->lmshift[1])?f->lmshift[0]:f->lmshift[1];
@ -728,34 +728,6 @@ GetContentsName( const contentflags_t &Contents ) {
}
}
int Contents_Priority(const contentflags_t &contents)
{
if (contents.extended & CFLAGS_DETAIL) {
return 5;
} else if (contents.extended & CFLAGS_DETAIL_ILLUSIONARY) {
return 3;
} else if (contents.extended & CFLAGS_DETAIL_FENCE) {
return 4;
} else if (contents.extended & CFLAGS_ILLUSIONARY_VISBLOCKER) {
return 2;
} else switch( contents.native ) {
case CONTENTS_SOLID: return 7;
case CONTENTS_SKY: return 6;
case CONTENTS_WATER: return 2;
case CONTENTS_SLIME: return 2;
case CONTENTS_LAVA: return 2;
case CONTENTS_EMPTY: return 1;
case 0: return 0;
default:
Error("Bad contents in face (%s)", __func__);
return 0;
}
}
/*
==================
LinkConvexFaces
@ -770,26 +742,29 @@ static void
LinkConvexFaces(surface_t *planelist, node_t *leafnode)
{
leafnode->faces = NULL;
leafnode->contents = { 0, 0 };
leafnode->planenum = PLANENUM_LEAF;
int count = 0;
std::optional<contentflags_t> contents;
for (surface_t *surf = planelist; surf; surf = surf->next) {
for (face_t *f = surf->faces; f; f = f->next) {
count++;
const int currentpri = Contents_Priority(leafnode->contents);
const int fpri = Contents_Priority(f->contents[0]);
const int currentpri = contents.has_value() ? contents->priority(options.target_version->game) : -1;
const int fpri = f->contents[0].priority(options.target_version->game);
if (fpri > currentpri) {
leafnode->contents = f->contents[0];
contents = f->contents[0];
}
// HACK: Handle structural covered by detail.
if (f->contents[0].extended & CFLAGS_STRUCTURAL_COVERED_BY_DETAIL) {
Q_assert(f->contents[0].native == CONTENTS_EMPTY);
Q_assert(f->contents[0].is_empty(options.target_version->game));
if (Contents_Priority({ CONTENTS_SOLID, CFLAGS_DETAIL }) > currentpri) {
leafnode->contents = { CONTENTS_SOLID, CFLAGS_DETAIL };
const contentflags_t solid_detail = options.target_version->game->create_solid_contents(CFLAGS_DETAIL);
if (solid_detail.priority(options.target_version->game) > currentpri) {
contents = solid_detail;
}
}
}
@ -798,9 +773,7 @@ LinkConvexFaces(surface_t *planelist, node_t *leafnode)
// NOTE: This is crazy..
// Liquid leafs get assigned liquid content types because of the
// "cosmetic" mirrored faces.
if (!leafnode->contents.native) {
leafnode->contents.native = CONTENTS_SOLID; // FIXME: Need to create CONTENTS_DETAIL sometimes?
}
leafnode->contents = contents.value_or(options.target_version->game->create_solid_contents()); // FIXME: Need to create CONTENTS_DETAIL sometimes?
if (leafnode->contents.extended & CFLAGS_ILLUSIONARY_VISBLOCKER) {
c_illusionary_visblocker++;
@ -810,20 +783,13 @@ LinkConvexFaces(surface_t *planelist, node_t *leafnode)
c_detail_illusionary++;
} else if (leafnode->contents.extended & CFLAGS_DETAIL) {
c_detail++;
} else switch (leafnode->contents.native) {
case CONTENTS_EMPTY:
} else if (leafnode->contents.is_empty(options.target_version->game)) {
c_empty++;
break;
case CONTENTS_SOLID:
} else if (leafnode->contents.is_solid(options.target_version->game)) {
c_solid++;
break;
case CONTENTS_WATER:
case CONTENTS_SLIME:
case CONTENTS_LAVA:
case CONTENTS_SKY:
} else if (leafnode->contents.is_liquid(options.target_version->game) || leafnode->contents.is_sky(options.target_version->game)) {
c_water++;
break;
default:
} else {
Error("Bad contents in face (%s)", __func__);
}
@ -983,11 +949,11 @@ SolidBSP(const mapentity_t *entity, surface_t *surfhead, bool midsplit)
}
headnode->children[0] = (node_t *)AllocMem(OTHER, sizeof(node_t), true);
headnode->children[0]->planenum = PLANENUM_LEAF;
headnode->children[0]->contents = { CONTENTS_EMPTY };
headnode->children[0]->contents = options.target_version->game->create_empty_contents();
headnode->children[0]->markfaces = (face_t **)AllocMem(OTHER, sizeof(face_t *), true);
headnode->children[1] = (node_t *)AllocMem(OTHER, sizeof(node_t), true);
headnode->children[1]->planenum = PLANENUM_LEAF;
headnode->children[1]->contents = { CONTENTS_EMPTY };
headnode->children[1]->contents = options.target_version->game->create_empty_contents();
headnode->children[1]->markfaces = (face_t **)AllocMem(OTHER, sizeof(face_t *), true);
return headnode;

View File

@ -292,8 +292,8 @@ GetEdge(mapentity_t *entity, const vec3_t p1, const vec3_t p2,
int v1, v2;
int i;
if (!face->contents[0].native)
Error("Face with 0 contents (%s)", __func__);
if (!face->contents[0].is_valid(options.target_version->game))
Error("Face with invalid contents (%s)", __func__);
v1 = GetVertex(entity, p1);
v2 = GetVertex(entity, p2);

View File

@ -48,8 +48,8 @@ AssertVanillaContentType(int content)
}
}
static int
RemapContentsForExport_(const contentflags_t &content)
static contentflags_t
RemapContentsForExport(const contentflags_t &content)
{
if (content.extended & CFLAGS_DETAIL_FENCE) {
/*
@ -58,40 +58,10 @@ RemapContentsForExport_(const contentflags_t &content)
*
* Normally solid leafs are not written and just referenced as leaf 0.
*/
return CONTENTS_SOLID;
return options.target_version->game->create_solid_contents();
}
return content.native;
}
static int
RemapContentsForExport(const contentflags_t &content)
{
int32_t contents = RemapContentsForExport_(content);
// TODO
if (options.target_version->game->id == GAME_QUAKE_II) {
switch (contents) {
case CONTENTS_EMPTY:
return 0;
case CONTENTS_SOLID:
case CONTENTS_SKY:
if (content.extended & CFLAGS_CLIP) {
return Q2_CONTENTS_PLAYERCLIP | Q2_CONTENTS_MONSTERCLIP;
}
return Q2_CONTENTS_SOLID;
case CONTENTS_WATER:
return Q2_CONTENTS_WATER;
case CONTENTS_SLIME:
return Q2_CONTENTS_SLIME;
case CONTENTS_LAVA:
return Q2_CONTENTS_LAVA;
default:
Error("dunno what to do with contents %i\n", content);
}
}
return contents;
return content;
}
/**
@ -225,7 +195,7 @@ ExportLeaf(mapentity_t *entity, node_t *node)
map.exported_leafs.push_back({});
mleaf_t *dleaf = &map.exported_leafs.back();
dleaf->contents = RemapContentsForExport(node->contents);
dleaf->contents = RemapContentsForExport(node->contents).native;
AssertVanillaContentType(dleaf->contents);
/*
@ -291,7 +261,7 @@ ExportDrawNodes(mapentity_t *entity, node_t *node)
if (node->children[i]->planenum == PLANENUM_LEAF) {
// In Q2, all leaves must have their own ID even if they share solidity.
// (probably for collision purposes? makes sense given they store leafbrushes)
if (options.target_version->game->id != GAME_QUAKE_II && node->children[i]->contents.native == CONTENTS_SOLID)
if (options.target_version->game->id != GAME_QUAKE_II && node->children[i]->contents.is_solid(options.target_version->game))
dnode->children[i] = -1;
else {
int nextLeafIndex = static_cast<int>(map.exported_leafs.size());
@ -368,7 +338,7 @@ BeginBSPFile(void)
// Leave room for leaf 0 (must be solid)
map.exported_leafs.push_back({});
map.exported_leafs.back().contents = RemapContentsForExport({ CONTENTS_SOLID });
map.exported_leafs.back().contents = options.target_version->game->create_solid_contents().native;
Q_assert(map.exported_leafs.size() == 1);
}