Mostly all native!!
This commit is contained in:
parent
004fcb57c1
commit
9800f4f738
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
201
qbsp/brush.cc
201
qbsp/brush.cc
|
|
@ -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 {
|
||||
|
|
|
|||
77
qbsp/csg4.cc
77
qbsp/csg4.cc
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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++)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue