Merge remote-tracking branch 'remotes/origin/qbsp-contentflags' into type-cleanup

# Conflicts:
#	bspinfo/bspinfo.cc
#	common/bspfile.cc
#	include/common/bspfile.hh
#	qbsp/brush.cc
#	qbsp/csg4.cc
#	qbsp/portals.cc
#	qbsp/qbsp.cc
#	qbsp/solidbsp.cc
#	qbsp/surfaces.cc
#	qbsp/writebsp.cc
#	vis/vis.cc
This commit is contained in:
Jonathan 2021-10-05 01:21:16 -04:00
commit 7590230111
17 changed files with 522 additions and 164 deletions

View File

@ -192,6 +192,33 @@ static void serialize_bsp(const bspdata_t &bspdata, const mbsp_t &bsp, const std
}
}
if (bsp.numfaces) {
json &faces = (j.emplace("faces", json::array())).first.value();
for (int32_t i = 0; i < bsp.numfaces; i++) {
json &face = faces.insert(faces.end(), json::object()).value();
auto &src_face = bsp.dfaces[i];
face.push_back({ "planenum", src_face.planenum });
face.push_back({ "side", src_face.side });
face.push_back({ "firstedge", src_face.firstedge });
face.push_back({ "numedges", src_face.numedges });
face.push_back({ "texinfo", src_face.texinfo });
face.push_back({ "styles", json::array({ src_face.styles[0], src_face.styles[1], src_face.styles[2], src_face.styles[3] }) });
face.push_back({ "lightofs", src_face.lightofs });
// for readibility, also output the actual vertices
auto verts = json::array();
for (int32_t k = 0; k < src_face.numedges; ++k) {
auto se = bsp.dsurfedges[src_face.firstedge + k];
uint32_t v = (se < 0) ? bsp.dedges[-se].v[1] : bsp.dedges[se].v[0];
auto dv = bsp.dvertexes[v];
verts.push_back(json::array({ dv.point[0], dv.point[1], dv.point[2] }));
}
face.push_back({ "vertices", verts });
}
}
if (!bsp.dclipnodes.empty()) {
json &clipnodes = (j.emplace("clipnodes", json::array())).first.value();
@ -203,6 +230,32 @@ static void serialize_bsp(const bspdata_t &bspdata, const mbsp_t &bsp, const std
}
}
if (bsp.numedges) {
json &edges = (j.emplace("edges", json::array())).first.value();
for (int32_t i = 0; i < bsp.numedges; i++) {
auto &src_edge = bsp.dedges[i];
edges.insert(edges.end(), json::array({src_edge.v[0], src_edge.v[1]}));
}
}
if (bsp.numleaffaces) {
json &leaffaces = (j.emplace("leaffaces", json::array())).first.value();
for (int32_t i = 0; i < bsp.numleaffaces; i++) {
leaffaces.insert(leaffaces.end(), bsp.dleaffaces[i]);
}
}
if (bsp.numsurfedges) {
json &surfedges = (j.emplace("surfedges", json::array())).first.value();
for (int32_t i = 0; i < bsp.numsurfedges; i++) {
surfedges.insert(surfedges.end(), bsp.dsurfedges[i]);
}
}
if (!bsp.dbrushsides.empty()) {
json &brushsides = (j.emplace("brushsides", json::array())).first.value();

View File

@ -195,13 +195,25 @@ struct gamedef_generic_t : public gamedef_t
bool surf_is_subdivided(const surfflags_t &) const { throw std::bad_cast(); }
contentflags_t cluster_contents(const contentflags_t &, const contentflags_t &) const { throw std::bad_cast(); }
surfflags_t surf_remap_for_export(const surfflags_t& flags) const {
throw std::bad_cast();
}
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_extended_contents(const int32_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(); }
@ -209,7 +221,21 @@ struct gamedef_generic_t : public gamedef_t
contentflags_t create_liquid_contents(const int32_t &, const int32_t &) const { throw std::bad_cast(); }
bool contents_are_liquid(const contentflags_t &) const { throw std::bad_cast(); }
bool contents_are_empty(const contentflags_t& contents) const {
throw std::bad_cast();
}
bool contents_are_solid(const contentflags_t& contents) const {
throw std::bad_cast();
}
bool contents_are_sky(const contentflags_t& contents) 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(); }
@ -234,28 +260,31 @@ struct gamedef_q1_like_t : public gamedef_t
bool surf_is_subdivided(const surfflags_t &flags) const { return !(flags.native & TEX_SPECIAL); }
contentflags_t cluster_contents(const contentflags_t &contents0, const contentflags_t &contents1) const
{
surfflags_t surf_remap_for_export(const surfflags_t& flags) const {
auto remapped = flags;
remapped.native &= TEX_SPECIAL;
return remapped;
}
contentflags_t cluster_contents(const contentflags_t &contents0, const contentflags_t &contents1) const {
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 create_empty_contents(merged_flags);
return create_empty_contents();
if (contents0.native >= CONTENTS_LAVA && contents0.native <= CONTENTS_WATER)
return create_liquid_contents(contents0.native, merged_flags);
return create_liquid_contents(contents0.native);
if (contents1.native >= CONTENTS_LAVA && contents1.native <= CONTENTS_WATER)
return create_liquid_contents(contents1.native, merged_flags);
return create_liquid_contents(contents1.native);
if (contents0.native == CONTENTS_SKY || contents1.native == CONTENTS_SKY)
return create_sky_contents(merged_flags);
return create_sky_contents();
return create_solid_contents(merged_flags);
return create_solid_contents();
}
int32_t get_content_type(const contentflags_t &contents) const { return contents.native; }
@ -264,10 +293,10 @@ struct gamedef_q1_like_t : public gamedef_t
{
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_DETAIL_ILLUSIONARY) {
return 3;
} else if (contents.extended & CFLAGS_ILLUSIONARY_VISBLOCKER) {
return 2;
} else {
@ -286,16 +315,32 @@ struct gamedef_q1_like_t : public gamedef_t
default: FError("Bad contents in face"); return 0;
}
}
contentflags_t create_extended_contents(const int32_t &cflags) const {
return { 0, cflags };
}
contentflags_t create_empty_contents(const int32_t &cflags) const { return {CONTENTS_EMPTY, cflags}; }
contentflags_t create_empty_contents(const int32_t &cflags = 0) const {
Q_assert(!(cflags & CFLAGS_CONTENTS_MASK));
contentflags_t create_solid_contents(const int32_t &cflags) const { return {CONTENTS_SOLID, cflags}; }
return { CONTENTS_EMPTY, cflags };
}
contentflags_t create_sky_contents(const int32_t &cflags) const { return {CONTENTS_SKY, cflags}; }
contentflags_t create_solid_contents(const int32_t &cflags = 0) const {
Q_assert(!(cflags & CFLAGS_CONTENTS_MASK));
return { CONTENTS_SOLID, cflags };
}
contentflags_t create_sky_contents(const int32_t &cflags = 0) const {
Q_assert(!(cflags & CFLAGS_CONTENTS_MASK));
return { CONTENTS_SKY, cflags };
}
contentflags_t create_liquid_contents(const int32_t &liquid_type, const int32_t &cflags = 0) const {
Q_assert(!(cflags & CFLAGS_CONTENTS_MASK));
contentflags_t create_liquid_contents(const int32_t &liquid_type, const int32_t &cflags) const
{
return {liquid_type, cflags};
}
@ -304,12 +349,28 @@ struct gamedef_q1_like_t : public gamedef_t
return contents.native <= CONTENTS_WATER && contents.native >= CONTENTS_LAVA;
}
bool contents_are_valid(const contentflags_t &contents, bool strict) const { return contents.native <= 0; }
bool contents_are_valid(const contentflags_t &contents, bool strict) const {
if (!contents.native && !strict) {
return true;
}
bool portal_can_see_through(const contentflags_t &contents0, const contentflags_t &contents1) const
{
switch (contents.native) {
case CONTENTS_EMPTY:
case CONTENTS_SOLID:
case CONTENTS_WATER:
case CONTENTS_SLIME:
case CONTENTS_LAVA:
case CONTENTS_SKY:
return true;
default:
return false;
}
}
bool portal_can_see_through(const contentflags_t &contents0, const contentflags_t &contents1) const {
/* If contents values are the same and not solid, can see through */
return !(contents0.is_structural_solid(this) || contents1.is_structural_solid(this)) && contents0 == contents1;
return !(contents0.is_solid(this) || contents1.is_solid(this)) &&
contents0 == contents1;
}
std::string get_contents_display(const contentflags_t &contents) const
@ -394,8 +455,13 @@ struct gamedef_q2_t : public gamedef_t
bool surf_is_subdivided(const surfflags_t &flags) const { return !(flags.native & (Q2_SURF_WARP | Q2_SURF_SKY)); }
contentflags_t cluster_contents(const contentflags_t &contents0, const contentflags_t &contents1) const
{
surfflags_t surf_remap_for_export(const surfflags_t& flags) const {
auto remapped = flags;
// TODO: strip any illegal flags off remapped.native
return remapped;
}
contentflags_t cluster_contents(const contentflags_t &contents0, const contentflags_t &contents1) const {
contentflags_t c = {contents0.native | contents1.native, contents0.extended | contents1.extended};
// a cluster may include some solid detail areas, but
@ -435,7 +501,13 @@ struct gamedef_q2_t : public gamedef_t
}
}
contentflags_t create_empty_contents(const int32_t &cflags) const { return {0, cflags}; }
contentflags_t create_extended_contents(const int32_t &cflags) const {
return { 0, cflags };
}
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}; }
@ -558,7 +630,7 @@ const bspversion_t bspver_qbism{Q2_QBISMIDENT, Q2_BSPVERSION, "qbism", "Quake II
bool contentflags_t::types_equal(const contentflags_t &other, const gamedef_t *game) const
{
return (extended & CFLAGS_DETAIL_MASK) == (other.extended & CFLAGS_DETAIL_MASK) &&
return (extended & CFLAGS_CONTENTS_MASK) == (other.extended & CFLAGS_CONTENTS_MASK) &&
game->get_content_type(*this) == game->get_content_type(other);
}

View File

@ -564,6 +564,8 @@ struct dplane_t
#define CFLAGS_ILLUSIONARY_VISBLOCKER (1 << 11)
// all of the detail values
#define CFLAGS_DETAIL_MASK (CFLAGS_DETAIL | CFLAGS_DETAIL_ILLUSIONARY | CFLAGS_DETAIL_FENCE)
// all of the special content types
#define CFLAGS_CONTENTS_MASK (CFLAGS_HINT | CFLAGS_CLIP | CFLAGS_ORIGIN | CFLAGS_DETAIL_MASK | CFLAGS_ILLUSIONARY_VISBLOCKER)
struct gamedef_t;
@ -575,13 +577,6 @@ struct contentflags_t
// extra flags, specific to BSP only
int32_t extended;
// merge these content flags with other, and use
// their native contents.
constexpr contentflags_t merge(const contentflags_t &other) const
{
return {other.native, extended | other.extended};
}
constexpr bool operator==(const contentflags_t &other) const
{
return native == other.native && extended == other.extended;
@ -596,25 +591,30 @@ struct contentflags_t
}
bool is_empty(const gamedef_t *game) const;
// solid, not detail or any other extended content types
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 is_hint() const { return extended & CFLAGS_HINT; }
constexpr bool is_clip() const {
return extended & CFLAGS_CLIP;
}
constexpr bool is_origin() const {
return extended & CFLAGS_ORIGIN;
}
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); }
constexpr bool is_fence() const {
return (extended & (CFLAGS_DETAIL_FENCE | CFLAGS_DETAIL_ILLUSIONARY)) != 0;
}
// check if this content's `type` - which is distinct from various
// flags that turn things on/off - match. Exactly what the native
@ -1911,9 +1911,11 @@ 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 surfflags_t surf_remap_for_export(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_extended_contents(const int32_t &cflags = 0) 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;

View File

@ -765,36 +765,10 @@ static bool 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_game->create_empty_contents(CFLAGS_ORIGIN));
else if (!Q_strcasecmp(texname, "hint"))
flags = flags.merge(options.target_game->create_empty_contents(CFLAGS_HINT));
else if (!Q_strcasecmp(texname, "clip"))
flags = flags.merge(options.target_game->create_solid_contents(CFLAGS_CLIP));
else if (texname[0] == '*') {
if (!Q_strncasecmp(texname + 1, "lava", 4))
flags = flags.merge(options.target_game->create_liquid_contents(CONTENTS_LAVA));
else if (!Q_strncasecmp(texname + 1, "slime", 5))
flags = flags.merge(options.target_game->create_liquid_contents(CONTENTS_SLIME));
else
flags = flags.merge(options.target_game->create_liquid_contents(CONTENTS_WATER));
} else if (!Q_strncasecmp(texname, "sky", 3))
flags = flags.merge(options.target_game->create_sky_contents());
else
return false;
return true;
}
static contentflags_t Brush_GetContents_Q1(const mapbrush_t *mapbrush, const contentflags_t &base_contents)
static contentflags_t
Brush_GetContents_Q1(const mapbrush_t *mapbrush)
{
const char *texname;
contentflags_t contents = base_contents;
// check for strong content indicators
for (int i = 0; i < mapbrush->numfaces; i++) {
@ -802,20 +776,34 @@ static contentflags_t Brush_GetContents_Q1(const mapbrush_t *mapbrush, const con
const mtexinfo_t &texinfo = map.mtexinfos.at(mapface.texinfo);
texname = map.miptexTextureName(texinfo.miptex).c_str();
if (AdjustContentsFromName(texname, contents)) {
return contents;
if (!Q_strcasecmp(texname, "origin"))
return options.target_game->create_extended_contents(CFLAGS_ORIGIN);
else if (!Q_strcasecmp(texname, "hint"))
return options.target_game->create_extended_contents(CFLAGS_HINT);
else if (!Q_strcasecmp(texname, "clip"))
return options.target_game->create_extended_contents(CFLAGS_CLIP);
else if (texname[0] == '*') {
if (!Q_strncasecmp(texname + 1, "lava", 4))
return options.target_game->create_liquid_contents(CONTENTS_LAVA);
else if (!Q_strncasecmp(texname + 1, "slime", 5))
return options.target_game->create_liquid_contents(CONTENTS_SLIME);
else
return options.target_game->create_liquid_contents(CONTENTS_WATER);
}
else if (!Q_strncasecmp(texname, "sky", 3))
return options.target_game->create_sky_contents();
}
// and anything else is assumed to be a regular solid.
return contents.merge(options.target_game->create_solid_contents());
return options.target_game->create_solid_contents();
}
static contentflags_t Brush_GetContents_Q2(const mapbrush_t *mapbrush, const contentflags_t &base_contents)
static contentflags_t
Brush_GetContents_Q2 (const mapbrush_t *mapbrush)
{
bool is_trans = false;
bool is_hint = false;
contentflags_t contents = base_contents.merge({mapbrush->face(0).contents});
contentflags_t contents = { mapbrush->face(0).contents };
for (int i = 0; i < mapbrush->numfaces; i++) {
const mapface_t &mapface = mapbrush->face(i);
@ -1058,21 +1046,13 @@ void Brush_LoadEntity(mapentity_t *dst, const mapentity_t *src, const int hullnu
/* Origin brush support */
rotation_t rottype = rotation_t::none;
const bool func_illusionary_visblocker = (0 == Q_strcasecmp(classname, "func_illusionary_visblocker"));
contentflags_t base_contents = options.target_game->create_empty_contents();
if (func_illusionary_visblocker) {
base_contents.extended |= CFLAGS_ILLUSIONARY_VISBLOCKER;
}
// TODO: move to game
auto Brush_GetContents = (options.target_game->id == GAME_QUAKE_II) ? Brush_GetContents_Q2 : Brush_GetContents_Q1;
for (int i = 0; i < src->nummapbrushes; i++) {
const mapbrush_t *mapbrush = &src->mapbrush(i);
const contentflags_t contents = Brush_GetContents(mapbrush, base_contents);
if (contents.extended & CFLAGS_ORIGIN) {
const contentflags_t contents = Brush_GetContents(mapbrush);
if (contents.is_origin()) {
if (dst == pWorldEnt()) {
LogPrint("WARNING: Ignoring origin brush in worldspawn\n");
continue;
@ -1127,18 +1107,17 @@ void Brush_LoadEntity(mapentity_t *dst, const mapentity_t *src, const int hullnu
}
/* _mirrorinside key (for func_water etc.) */
if (atoi(ValueForKey(src, "_mirrorinside"))) {
base_contents.extended |= CFLAGS_BMODEL_MIRROR_INSIDE;
}
const bool mirrorinside = !!atoi(ValueForKey(src, "_mirrorinside"));
/* _noclipfaces */
if (atoi(ValueForKey(src, "_noclipfaces"))) {
base_contents.extended |= CFLAGS_NO_CLIPPING_SAME_TYPE;
}
const bool noclipfaces = !!atoi(ValueForKey(src, "_noclipfaces"));
const bool func_illusionary_visblocker =
(0 == Q_strcasecmp(classname, "func_illusionary_visblocker"));
for (i = 0; i < src->nummapbrushes; i++, mapbrush++) {
mapbrush = &src->mapbrush(i);
contentflags_t contents = Brush_GetContents(mapbrush, base_contents);
contentflags_t contents = Brush_GetContents(mapbrush);
// per-brush settings
bool detail = Brush_IsDetail(mapbrush);
@ -1151,7 +1130,7 @@ void Brush_LoadEntity(mapentity_t *dst, const mapentity_t *src, const int hullnu
detail_fence |= all_detail_fence;
/* "origin" brushes always discarded */
if (contents.extended & CFLAGS_ORIGIN)
if (contents.is_origin())
continue;
/* -omitdetail option omits all types of detail */
@ -1165,12 +1144,11 @@ void Brush_LoadEntity(mapentity_t *dst, const mapentity_t *src, const int hullnu
/* turn solid brushes into detail, if we're in hull0 */
if (hullnum <= 0 && contents.is_solid(options.target_game)) {
if (detail) {
contents.extended |= CFLAGS_DETAIL;
contents = options.target_game->create_extended_contents(CFLAGS_DETAIL);
} else if (detail_illusionary) {
contents = contents.merge(options.target_game->create_empty_contents(CFLAGS_DETAIL_ILLUSIONARY));
contents = options.target_game->create_extended_contents(CFLAGS_DETAIL_ILLUSIONARY);
} else if (detail_fence) {
contents = contents.merge(
options.target_game->create_empty_contents(CFLAGS_DETAIL_FENCE)); // fences need to generate leaves
contents = options.target_game->create_extended_contents(CFLAGS_DETAIL_FENCE);
}
}
@ -1185,7 +1163,7 @@ void Brush_LoadEntity(mapentity_t *dst, const mapentity_t *src, const int hullnu
* include them in the model bounds so collision detection works
* correctly.
*/
if (contents.extended & CFLAGS_CLIP) {
if (contents.is_clip()) {
if (hullnum == 0) {
brush_t *brush = LoadBrush(src, mapbrush, contents, rotate_offset, rottype, hullnum);
if (brush) {
@ -1196,8 +1174,7 @@ void Brush_LoadEntity(mapentity_t *dst, const mapentity_t *src, const int hullnu
}
// for hull1, 2, etc., convert clip to CONTENTS_SOLID
if (hullnum > 0) {
contents = contents.merge(options.target_game->create_solid_contents());
contents.extended &= ~CFLAGS_CLIP;
contents = options.target_game->create_solid_contents();
}
// if hullnum is -1 (bspx brush export), leave it as CONTENTS_CLIP
}
@ -1206,16 +1183,12 @@ void Brush_LoadEntity(mapentity_t *dst, const mapentity_t *src, const int hullnu
if (contents.is_hint()) {
if (hullnum > 0)
continue;
contents = contents.merge(options.target_game->create_empty_contents());
contents = options.target_game->create_empty_contents();
}
/* entities never use water merging */
if (dst != pWorldEnt()) {
// FIXME: the old code here was just `contents = CONTENTS_SOLID`.
// which would also clear CONTENTS_CLIP. Now CONTENTS_CLIP is an extended flag
// so we need to unset it explicitly.
contents = contents.merge(options.target_game->create_solid_contents());
contents.extended &= ~CFLAGS_CLIP;
contents = options.target_game->create_solid_contents();
}
/* Hack to turn bmodels with "_mirrorinside" into func_detail_fence in hull 0.
@ -1226,19 +1199,28 @@ void Brush_LoadEntity(mapentity_t *dst, const mapentity_t *src, const int hullnu
before writing the bsp, and bmodels normally have CONTENTS_SOLID as their
contents type.
*/
if (dst != pWorldEnt() && hullnum <= 0 && (contents.extended & CFLAGS_BMODEL_MIRROR_INSIDE)) {
contents = contents.merge(options.target_game->create_empty_contents(CFLAGS_DETAIL_FENCE));
if (dst != pWorldEnt() && hullnum <= 0 && mirrorinside) {
contents = options.target_game->create_extended_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.is_solid(options.target_game) && !contents.is_sky(options.target_game))
continue;
/* sky brushes are solid in the collision hulls */
if (hullnum > 0 && contents.is_sky(options.target_game))
contents = contents.merge(options.target_game->create_solid_contents());
contents = options.target_game->create_solid_contents();
// apply extended flags
if (mirrorinside) {
contents.extended |= CFLAGS_BMODEL_MIRROR_INSIDE;
}
if (noclipfaces) {
contents.extended |= CFLAGS_NO_CLIPPING_SAME_TYPE;
}
if (func_illusionary_visblocker) {
contents.extended |= CFLAGS_ILLUSIONARY_VISBLOCKER;
}
brush_t *brush = LoadBrush(src, mapbrush, contents, rotate_offset, rottype, hullnum);
if (!brush)
@ -1247,7 +1229,13 @@ void Brush_LoadEntity(mapentity_t *dst, const mapentity_t *src, const int hullnu
dst->numbrushes++;
brush->lmshift = lmshift;
if (brush->contents.is_detail(CFLAGS_DETAIL)) {
if (brush->contents.is_solid(options.target_game)) {
brush->next = dst->solid;
dst->solid = brush;
} else if (brush->contents.is_sky(options.target_game)) {
brush->next = dst->sky;
dst->sky = brush;
} else if (brush->contents.is_detail(CFLAGS_DETAIL)) {
brush->next = dst->detail;
dst->detail = brush;
} else if (brush->contents.is_detail(CFLAGS_DETAIL_ILLUSIONARY)) {
@ -1256,12 +1244,6 @@ void Brush_LoadEntity(mapentity_t *dst, const mapentity_t *src, const int hullnu
} else if (brush->contents.is_detail(CFLAGS_DETAIL_FENCE)) {
brush->next = dst->detail_fence;
dst->detail_fence = brush;
} else if (brush->contents.is_solid(options.target_game) && !(contents.extended & CFLAGS_CLIP)) {
brush->next = dst->solid;
dst->solid = brush;
} else if (brush->contents.is_sky(options.target_game)) {
brush->next = dst->sky;
dst->sky = brush;
} else {
brush->next = dst->liquid;
dst->liquid = brush;

View File

@ -352,8 +352,8 @@ void SaveFacesToPlaneList(face_t *facelist, bool mirror, std::map<int, face_t *>
// 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].is_detail() || (face->contents[1].extended & CFLAGS_WAS_ILLUSIONARY)) ||
(options.fContentHack && face->contents[1].is_structural_solid(options.target_game))) {
if ((face->contents[1].is_detail() || (face->contents[1].extended & CFLAGS_WAS_ILLUSIONARY))
|| (options.fContentHack && face->contents[1].is_solid(options.target_game))) {
// if CFLAGS_BMODEL_MIRROR_INSIDE is set, never change to skip
if (!(face->contents[1].extended & CFLAGS_BMODEL_MIRROR_INSIDE)) {
@ -397,7 +397,7 @@ contents override the face inside contents.
*/
static void SaveInsideFaces(face_t *face, const brush_t *clipbrush, face_t **savelist)
{
Q_assert(!clipbrush->contents.is_structural_solid(options.target_game));
Q_assert(!clipbrush->contents.is_solid(options.target_game));
face_t *next;
@ -408,8 +408,8 @@ static void SaveInsideFaces(face_t *face, const brush_t *clipbrush, face_t **sav
next = face->next;
face->contents[0] = clipbrush->contents;
if (face->contents[1].is_structural_sky_or_solid(options.target_game) &&
clipbrush->contents.is_detail(CFLAGS_DETAIL)) {
if ((face->contents[1].is_solid(options.target_game) || face->contents[1].is_sky(options.target_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.
@ -424,27 +424,32 @@ static void SaveInsideFaces(face_t *face, const brush_t *clipbrush, face_t **sav
// marked as empty here, and the detail faces have their "back"
// marked as detail.
face->contents[0] = options.target_game->create_empty_contents(CFLAGS_STRUCTURAL_COVERED_BY_DETAIL);
face->contents[0] = options.target_game->create_empty_contents();
face->contents[0].extended |= 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].is_structural_sky_or_solid(options.target_game) &&
face->contents[0].is_detail(CFLAGS_DETAIL)));
Q_assert(!(face->contents[1].is_solid(options.target_game) && face->contents[0].is_detail(CFLAGS_DETAIL)));
Q_assert(!(face->contents[1].is_sky(options.target_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].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].is_empty(options.target_game)) {
face->contents[1] = clipbrush->contents;
}
if (face->contents[1].is_detail(CFLAGS_DETAIL_ILLUSIONARY)) {
bool wasMirrorInside = !!(face->contents[1].extended & CFLAGS_BMODEL_MIRROR_INSIDE);
face->contents[1] = clipbrush->contents;
face->contents[1].extended |= CFLAGS_WAS_ILLUSIONARY;
if (wasMirrorInside) {
face->contents[1].extended |= CFLAGS_BMODEL_MIRROR_INSIDE;
}
}
face->next = *savelist;
*savelist = face;
@ -569,7 +574,7 @@ surface_t *CSGFaces(const mapentity_t *entity)
overwrite = true;
continue;
}
if (clipbrush->contents.is_hint()) {
if (clipbrush->contents.is_empty(options.target_game)) {
/* Ensure hint never clips anything */
continue;
}
@ -585,8 +590,6 @@ surface_t *CSGFaces(const mapentity_t *entity)
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.types_equal(brush->contents, options.target_game) &&
!clipbrush->contents.clips_same_type()) {
/* _noclipfaces key */
@ -630,16 +633,14 @@ surface_t *CSGFaces(const mapentity_t *entity)
*
* FIXME: clean this up, the predicate seems to be "can you see 'brush' from inside 'clipbrush'"
*/
if ((brush->contents.is_structural_solid(options.target_game) &&
!clipbrush->contents.is_structural_solid(options.target_game))
if ((brush->contents.is_solid(options.target_game) && !clipbrush->contents.is_solid(options.target_game))
|| (brush->contents.is_structural_sky(options.target_game) &&
!clipbrush->contents.is_structural_sky_or_solid(options.target_game))
|| (brush->contents.is_sky(options.target_game) && (!clipbrush->contents.is_solid(options.target_game)
&& !clipbrush->contents.is_sky(options.target_game)))
|| ((brush->contents.is_solid(options.target_game) && brush->contents.is_detail(CFLAGS_DETAIL)) &&
(!clipbrush->contents.is_solid(options.target_game) &&
!clipbrush->contents.is_sky(options.target_game) &&
!clipbrush->contents.is_detail(CFLAGS_DETAIL)))
|| (brush->contents.is_detail(CFLAGS_DETAIL) && (!clipbrush->contents.is_solid(options.target_game)
&& !clipbrush->contents.is_sky(options.target_game)
&& !clipbrush->contents.is_detail(CFLAGS_DETAIL)))
|| (brush->contents.is_liquid(options.target_game) &&
clipbrush->contents.is_detail(CFLAGS_DETAIL_ILLUSIONARY))

View File

@ -322,7 +322,7 @@ static void ClearOutFaces(node_t *node)
}
// visit the leaf
if (!node->contents.is_structural_solid(options.target_game)) {
if (!node->contents.is_solid(options.target_game)) {
return;
}
@ -348,7 +348,8 @@ static void OutLeafsToSolid_r(node_t *node, int *outleafs_count)
return;
// Don't fill sky, or count solids as outleafs
if (node->contents.is_structural_sky_or_solid(options.target_game))
if (node->contents.is_solid(options.target_game)
|| node->contents.is_sky(options.target_game))
return;
// Now check all faces touching the leaf. If any of them are partially going into the occupied part of the map,

View File

@ -212,7 +212,7 @@ static void NumberLeafs_r(node_t *node, portal_state_t *state, int cluster)
return;
}
if (node->contents.is_structural_solid(options.target_game)) {
if (node->contents.is_solid(options.target_game)) {
/* solid block, viewpoint never inside */
node->visleafnum = -1;
node->viscluster = -1;

View File

@ -38,7 +38,8 @@ options_t options;
bool node_t::opaque() const
{
return contents.is_structural_sky_or_solid(options.target_game);
return contents.is_sky(options.target_game)
|| contents.is_solid(options.target_game);
}
// a simple tree structure used for leaf brush
@ -894,10 +895,15 @@ void BSPX_Brushes_AddModel(struct bspxbrushes_s *ctx, int modelnum, brush_t *bru
// case CONTENTS_LADDER:
// perbrush.contents = -16;
// break;
default:
default: {
if (b->contents.is_clip()) {
perbrush.contents = -8;
} else {
LogPrint("WARNING: Unknown contents: {}. Translating to solid.\n", b->contents.to_string(options.target_game));
perbrush.contents = CONTENTS_SOLID;
}
break;
}
}
perbrush.contents = LittleShort(perbrush.contents);
perbrush.numfaces = LittleShort(perbrush.numfaces);

View File

@ -62,15 +62,30 @@ void ConvertNodeToLeaf(node_t *node, const contentflags_t &contents)
void DetailToSolid(node_t *node)
{
if (node->planenum != PLANENUM_LEAF) {
if (node->planenum == PLANENUM_LEAF) {
if (options.target_game->id == GAME_QUAKE_II) {
return;
}
// We need to remap CONTENTS_DETAIL to a standard quake content type
if (node->contents.is_detail(CFLAGS_DETAIL)) {
node->contents = options.target_game->create_solid_contents();
} else if (node->contents.is_detail(CFLAGS_DETAIL_ILLUSIONARY)) {
node->contents = options.target_game->create_empty_contents();
}
/* N.B.: CONTENTS_DETAIL_FENCE is not remapped to CONTENTS_SOLID until the very last moment,
* because we want to generate a leaf (if we set it to CONTENTS_SOLID now it would use leaf 0).
*/
return;
} else {
DetailToSolid(node->children[0]);
DetailToSolid(node->children[1]);
// 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.is_structural_solid(options.target_game) &&
node->children[1]->contents.is_structural_solid(options.target_game)) {
if (node->children[0]->contents.is_solid(options.target_game)
&& node->children[1]->contents.is_solid(options.target_game)) {
// This discards any faces on-node. Should be safe (?)
ConvertNodeToLeaf(node, options.target_game->create_solid_contents());
}
@ -670,7 +685,7 @@ static void LinkConvexFaces(surface_t *planelist, node_t *leafnode)
if (f->contents[0].extended & CFLAGS_STRUCTURAL_COVERED_BY_DETAIL) {
Q_assert(f->contents[0].is_empty(options.target_game));
const contentflags_t solid_detail = options.target_game->create_solid_contents(CFLAGS_DETAIL);
const contentflags_t solid_detail = options.target_game->create_extended_contents(CFLAGS_DETAIL);
if (solid_detail.priority(options.target_game) > currentpri) {
contents = solid_detail;

View File

@ -283,7 +283,9 @@ inline size_t GetEdge(mapentity_t *entity, const qvec3d &p1, const qvec3d &p2, c
auto it = hashedges.find(edge_hash_key);
if (it != hashedges.end()) {
for (const int i : it->second) {
if (pEdgeFaces1[i] == NULL && pEdgeFaces0[i]->contents[0] == face->contents[0]) {
edge = &map.exported_edges.at(i);
if (pEdgeFaces1[i] == NULL
&& pEdgeFaces0[i]->contents[0].native == face->contents[0].native) {
pEdgeFaces1[i] = face;
return -i;
}

View File

@ -83,10 +83,13 @@ size_t ExportMapTexinfo(size_t texinfonum)
gtexinfo_t &dest = map.bsp.texinfo.emplace_back();
dest.flags = src.flags;
dest.miptex = src.miptex;
dest.vecs = src.vecs;
// make sure we don't write any non-native flags.
// e.g. Quake only accepts 0 or TEX_SPECIAL.
dest->flags = options.target_game->surf_remap_for_export(src->flags);
// TODO: warn if dest->flags.native != src->flags.native
dest->miptex = src->miptex;
dest.vecs = src.vecs;
strcpy(dest.texture.data(), map.texinfoTextureName(texinfonum).c_str());
dest.value = map.miptex[src.miptex].value;

View File

@ -35,7 +35,9 @@ light invalid_texture_axes.map || exit 1
# regenerate the expected hashes, but check that the .bsp's still
# work in game at the same time.
HASH_CHECK_BSPS="e1m1-bsp29.bsp \
HASH_CHECK_BSPS="qbsp_func_detail.bsp \
qbsp_func_detail_illusionary_plus_water.bsp \
e1m1-bsp29.bsp \
e1m1-bsp2.bsp \
e1m1-2psb.bsp \
e1m1-hexen2.bsp \
@ -46,6 +48,12 @@ e1m1-bspxbrushes.bsp \
e1m1-bsp29-onlyents.bsp \
qbspfeatures.bsp"
HASH_CHECK_PRTS=${HASH_CHECK_BSPS//.bsp/.prt}
# smaller test maps for specific features/combinations
qbsp -noverbose qbsp_func_detail.map || exit 1
qbsp -noverbose qbsp_func_detail_illusionary_plus_water.bsp || exit 1
qbsp -noverbose quake_map_source/E1M1.map e1m1-bsp29.bsp || exit 1
qbsp -noverbose -bsp2 quake_map_source/E1M1.map e1m1-bsp2.bsp || exit 1
qbsp -noverbose -2psb quake_map_source/E1M1.map e1m1-2psb.bsp || exit 1
@ -66,18 +74,22 @@ qbsp -onlyents E1M1-edited-ents.map e1m1-bsp29-onlyents.bsp || exit 1
qbsp -noverbose qbspfeatures.map || exit 1
if [[ $UPDATE_HASHES -ne 0 ]]; then
sha256sum ${HASH_CHECK_BSPS} > qbsp.sha256sum || exit 1
sha256sum ${HASH_CHECK_BSPS} ${HASH_CHECK_PRTS} > qbsp.sha256sum || exit 1
else
sha256sum --strict --check qbsp.sha256sum || exit 1
fi
# now run vis
# FIXME: vis output is nondeterministic when run with multiple threads!
# since vis is slower, launch all as background processes, and then wait for all of them to finish
# FIXME: vis output is nondeterministic when run with multiple threads, so force 1 thread per process
for bsp in ${HASH_CHECK_BSPS}; do
vis -nostate -threads 1 ${bsp} || exit 1
vis -nostate -threads 1 ${bsp} &
done
# we don't get the exit status this way, but the hash check will test the resulting visdata
wait
if [[ $UPDATE_HASHES -ne 0 ]]; then
sha256sum ${HASH_CHECK_BSPS} > qbsp-vis.sha256sum || exit 1
else

View File

@ -1,3 +1,5 @@
416e68cd92c817f206c11006db21bd6b0cbc330eee63bdfc66afc5152b406760 *qbsp_func_detail.bsp
c338b813b6529e5cd790720e3bc6a9e8f74e02e3348db04756174403f3331b8c *qbsp_func_detail_illusionary_plus_water.bsp
c9683e945bb01528a768653cdb79584dfbc8be6f76b712cbf0d24482a4ae3cc3 *e1m1-bsp29.bsp
a8024b07d48abc0553ff3628d6d4cb62138f67808b425b833e5643068231c2e4 *e1m1-bsp2.bsp
cb11d4f40d92c9d349fd11f9e4901e9cd9aa82db9971181a615c197f75f4b4a6 *e1m1-2psb.bsp

View File

@ -1,3 +1,5 @@
45d60368c37ee3523f9a1dbc140f34ae7728fc13d3cb8a43e7b22caefe46edb1 *qbsp_func_detail.bsp
565e9aef399341eef99fc37212d7ab314df8f6dd850a8ca5a471661088d7d3bc *qbsp_func_detail_illusionary_plus_water.bsp
d289427e3c8a7046dce509a83401f6732a9c5f8194f8beb3eee431e33215656c *e1m1-bsp29.bsp
85002a12afa023d850199e205f01fc58de2743f97aea9b2a22a3b0ac3ce8f7ad *e1m1-bsp2.bsp
832ec9aff302ddfcc5ab4fe0911afec0b4d57c60e02dc1c8f61e1edbf8f53eda *e1m1-2psb.bsp
@ -8,3 +10,15 @@ d289427e3c8a7046dce509a83401f6732a9c5f8194f8beb3eee431e33215656c *e1m1-bsp29.bsp
015c5fb3f350dfb10c031ed3ae44b3e094fbde38c48dec0897d0710fcbc7666c *e1m1-bspxbrushes.bsp
af7bc468d76aa1b11d1881a3378877059c6fd33fb37bea555d332e17d0e1e23c *e1m1-bsp29-onlyents.bsp
9f6db65a0c8a3eaa369deb3bb3168b5da69f23eb80a12788ff50d3c30b8a469a *qbspfeatures.bsp
abf3633d5a6d0e167ce9bacec476d3408c8240df5f3d72d14867cd201d4e3674 *qbsp_func_detail.prt
c0995c6b92256fa048c1a755ebe7e07f5fae33cb64e3c53adc234380fe44f267 *qbsp_func_detail_illusionary_plus_water.prt
08349ab23c97120c41493d8fb00b9a6c41553aba64b1710ba11cc5bdfbe51e45 *e1m1-bsp29.prt
08349ab23c97120c41493d8fb00b9a6c41553aba64b1710ba11cc5bdfbe51e45 *e1m1-bsp2.prt
08349ab23c97120c41493d8fb00b9a6c41553aba64b1710ba11cc5bdfbe51e45 *e1m1-2psb.prt
08349ab23c97120c41493d8fb00b9a6c41553aba64b1710ba11cc5bdfbe51e45 *e1m1-hexen2.prt
08349ab23c97120c41493d8fb00b9a6c41553aba64b1710ba11cc5bdfbe51e45 *e1m1-hexen2-bsp2.prt
08349ab23c97120c41493d8fb00b9a6c41553aba64b1710ba11cc5bdfbe51e45 *e1m1-hexen2-2psb.prt
08349ab23c97120c41493d8fb00b9a6c41553aba64b1710ba11cc5bdfbe51e45 *e1m1-hlbsp.prt
08349ab23c97120c41493d8fb00b9a6c41553aba64b1710ba11cc5bdfbe51e45 *e1m1-bspxbrushes.prt
08349ab23c97120c41493d8fb00b9a6c41553aba64b1710ba11cc5bdfbe51e45 *e1m1-bsp29-onlyents.prt
15361b57e8a0e8a3a9d949133eb53dadca91f03f1697997a9d36bad687edd2ab *qbspfeatures.prt

View File

@ -0,0 +1,97 @@
// Game: Quake
// Format: Valve
// entity 0
{
"mapversion" "220"
"classname" "worldspawn"
"wad" "deprecated/free_wad.wad;deprecated/fence.wad;deprecated/origin.wad;deprecated/hintskip.wad"
"_wateralpha" "0.5"
"_tb_def" "builtin:Quoth2.fgd"
// brush 0
{
( -176 -256 64 ) ( -176 -255 64 ) ( -176 -256 65 ) tsl_wall1 [ 0 -1 0 0 ] [ 0 0 -1 -32 ] 0 1 1
( -176 -432 64 ) ( -176 -432 65 ) ( -175 -432 64 ) tsl_wall1 [ 1 0 0 16 ] [ 0 0 -1 -32 ] 0 1 1
( -176 -256 96 ) ( -175 -256 96 ) ( -176 -255 96 ) tsl_wall1 [ -1 0 0 -16 ] [ 0 -1 0 0 ] 0 1 1
( -160 192 352 ) ( -160 193 352 ) ( -159 192 352 ) tsl_wall1 [ 1 0 0 16 ] [ 0 -1 0 0 ] 0 1 1
( -160 176 80 ) ( -159 176 80 ) ( -160 176 81 ) tsl_wall1 [ -1 0 0 -16 ] [ 0 0 -1 -32 ] 0 1 1
( -160 192 80 ) ( -160 192 81 ) ( -160 193 80 ) tsl_wall1 [ 0 1 0 0 ] [ 0 0 -1 -32 ] 0 1 1
}
// brush 1
{
( -160 176 88 ) ( -160 177 88 ) ( -160 176 89 ) tsl_wall1 [ 0 1.0000000000000002 0 -32 ] [ 0 0 -1.0000000000000002 -32 ] 0 1 1
( -160 176 88 ) ( -160 176 89 ) ( -159 176 88 ) tsl_wall1 [ -1 0 0 -16 ] [ 0 0 -1 -32 ] 0 1 1
( -160 176 96 ) ( -159 176 96 ) ( -160 177 96 ) tsl_wall1 [ -1.0000000000000002 0 0 -16 ] [ 0 1.0000000000000002 0 -40 ] 0 1 1
( 288 192 352 ) ( 288 193 352 ) ( 289 192 352 ) tsl_wall1 [ -1.0000000000000002 0 0 -16 ] [ 0 -1.0000000000000002 0 48 ] 0 1 1
( 288 192 96 ) ( 289 192 96 ) ( 288 192 97 ) tsl_wall1 [ -1 0 0 -16 ] [ 0 0 -1 -32 ] 0 1 1
( 288 192 96 ) ( 288 192 97 ) ( 288 193 96 ) tsl_wall1 [ 0 -1.0000000000000002 0 0 ] [ 0 0 -1.0000000000000002 -32 ] 0 1 1
}
// brush 2
{
( -160 -112 96 ) ( -160 -111 96 ) ( -160 -112 97 ) orangestuff8 [ 0 1 0 -16 ] [ 0 0 -1 0 ] 0 1 1
( -80 -432 80 ) ( -81 -432 80 ) ( -80 -432 81 ) orangestuff8 [ -1 0 0 16 ] [ 0 0 -1 0 ] 180 1 1
( -80 -432 80 ) ( -80 -431 80 ) ( -81 -432 80 ) orangestuff8 [ 1 0 0 -16 ] [ 0 -1 0 16 ] 180 1 1
( -160 -112 96 ) ( -161 -112 96 ) ( -160 -111 96 ) orangestuff8 [ -1 0 0 16 ] [ 0 -1 0 16 ] 180 1 1
( -160 176 96 ) ( -160 176 97 ) ( -161 176 96 ) orangestuff8 [ 1 0 0 -16 ] [ 0 0 -1 0 ] 180 1 1
( 288 -432 80 ) ( 288 -432 81 ) ( 288 -431 80 ) orangestuff8 [ 0 -1 0 16 ] [ 0 0 -1 0 ] 0 1 1
}
// brush 3
{
( -160 -448 88 ) ( -160 -447 88 ) ( -160 -448 89 ) tsl_wall1 [ 0 1.0000000000000002 0 80 ] [ 0 0 -1.0000000000000002 -32 ] 0 1 1
( -160 -448 88 ) ( -160 -448 89 ) ( -159 -448 88 ) tsl_wall1 [ -1 0 0 -16 ] [ 0 0 -1 -32 ] 0 1 1
( -160 -448 96 ) ( -159 -448 96 ) ( -160 -447 96 ) tsl_wall1 [ -1.0000000000000002 0 0 -16 ] [ 0 1.0000000000000002 0 72 ] 0 1 1
( 288 -432 352 ) ( 288 -431 352 ) ( 289 -432 352 ) tsl_wall1 [ -1.0000000000000002 0 0 -16 ] [ 0 -1.0000000000000002 0 -64 ] 0 1 1
( 288 -432 96 ) ( 289 -432 96 ) ( 288 -432 97 ) tsl_wall1 [ -1 0 0 -16 ] [ 0 0 -1 -32 ] 0 1 1
( 288 -432 96 ) ( 288 -432 97 ) ( 288 -431 96 ) tsl_wall1 [ 0 -1.0000000000000002 0 -112 ] [ 0 0 -1.0000000000000002 -32 ] 0 1 1
}
// brush 4
{
( -160 -256 352 ) ( -160 -255 352 ) ( -160 -256 353 ) orangestuff8 [ 0 0 -1.0000000000000002 0 ] [ 0 -1.0000000000000002 0 0 ] 180 1 1
( 288 -432 360 ) ( 288 -432 361 ) ( 289 -432 360 ) orangestuff8 [ -1.0000000000000002 0 0 0 ] [ 0 0 1.0000000000000002 -16 ] 180 1 1
( -160 -256 352 ) ( -159 -256 352 ) ( -160 -255 352 ) orangestuff8 [ -1 0 0 0 ] [ 0 -1 0 0 ] 180 1 1
( 288 176 368 ) ( 288 177 368 ) ( 289 176 368 ) orangestuff8 [ -1 0 0 0 ] [ 0 -1 0 0 ] 180 1 1
( 288 176 360 ) ( 289 176 360 ) ( 288 176 361 ) orangestuff8 [ -1.0000000000000002 0 0 0 ] [ 0 0 1.0000000000000002 -16 ] 180 1 1
( 288 176 360 ) ( 288 176 361 ) ( 288 177 360 ) orangestuff8 [ 0 0 1.0000000000000002 0 ] [ 0 -1.0000000000000002 0 0 ] 180 1 1
}
// brush 5
{
( 288 192 80 ) ( 288 193 80 ) ( 288 192 81 ) tsl_wall1 [ 0 1 0 0 ] [ 0 0 -1 -32 ] 0 1 1
( 304 -432 64 ) ( 303 -432 64 ) ( 304 -432 65 ) tsl_wall1 [ -1 0 0 16 ] [ 0 0 -1 -32 ] 180 1 1
( 304 -256 96 ) ( 304 -255 96 ) ( 303 -256 96 ) tsl_wall1 [ 1 0 0 -16 ] [ 0 -1 0 0 ] 180 1 1
( 304 -256 352 ) ( 303 -256 352 ) ( 304 -255 352 ) tsl_wall1 [ 1 0 0 -16 ] [ 0 -1 0 0 ] 180 1 1
( 288 176 80 ) ( 288 176 81 ) ( 287 176 80 ) tsl_wall1 [ 1 0 0 -16 ] [ 0 0 -1 -32 ] 180 1 1
( 304 -256 64 ) ( 304 -256 65 ) ( 304 -255 64 ) tsl_wall1 [ 0 -1 0 0 ] [ 0 0 -1 -32 ] 0 1 1
}
// brush 6
{
( 56 -8 96 ) ( 56 -7 96 ) ( 56 -8 97 ) tsl_wall1 [ 0 1.0000000000000002 0 -120 ] [ 0 0 -1.0000000000000002 -32 ] 0 1 1
( 56 -8 96 ) ( 56 -8 97 ) ( 57 -8 96 ) tsl_wall1 [ -1 0 0 -16 ] [ 0 0 -1 -32 ] 0 1 1
( 56 -8 96 ) ( 57 -8 96 ) ( 56 -7 96 ) tsl_wall1 [ -1.0000000000000002 0 0 -16 ] [ 0 1.0000000000000002 0 -48 ] 0 1 1
( 72 176 168 ) ( 72 177 168 ) ( 73 176 168 ) tsl_wall1 [ -1.0000000000000002 0 0 -16 ] [ 0 -1.0000000000000002 0 40 ] 0 1 1
( 72 176 104 ) ( 73 176 104 ) ( 72 176 105 ) tsl_wall1 [ -1 0 0 -16 ] [ 0 0 -1 -32 ] 0 1 1
( 72 176 104 ) ( 72 176 105 ) ( 72 177 104 ) tsl_wall1 [ 0 -1.0000000000000002 0 88 ] [ 0 0 -1.0000000000000002 -32 ] 0 1 1
}
}
// entity 1
{
"classname" "light"
"origin" "72 -136 168"
"light" "3000"
}
// entity 2
{
"classname" "info_player_start"
"origin" "-88 -64 120"
}
// entity 3
{
"classname" "func_detail"
// brush 0
{
( 32 -120 96 ) ( 32 -119 96 ) ( 32 -120 97 ) orangestuff8 [ 0 0 -1.0000000000000002 48 ] [ 0 -1.0000000000000002 0 -24 ] 180 1 1
( 16 -88 96 ) ( 16 -88 97 ) ( 17 -88 96 ) orangestuff8 [ -1.0000000000000002 0 0 -32 ] [ 0 0 -1.0000000000000002 48 ] 180 1 1
( 16 -120 96 ) ( 17 -120 96 ) ( 16 -119 96 ) orangestuff8 [ -1 0 0 -32 ] [ 0 -1 0 -24 ] 180 1 1
( 80 -40 104 ) ( 80 -39 104 ) ( 81 -40 104 ) +0fan [ -1 0 0 -32 ] [ 0 -1 0 -24 ] 180 1 1
( 80 -24 112 ) ( 81 -24 112 ) ( 80 -24 113 ) orangestuff8 [ -1.0000000000000002 0 0 -32 ] [ 0 0 1.0000000000000002 -16 ] 180 1 1
( 96 -40 112 ) ( 96 -40 113 ) ( 96 -39 112 ) orangestuff8 [ 0 0 1.0000000000000002 -16 ] [ 0 -1.0000000000000002 0 -24 ] 180 1 1
}
}

View File

@ -0,0 +1,96 @@
// Game: Quake
// Format: Valve
// entity 0
{
"mapversion" "220"
"classname" "worldspawn"
"wad" "deprecated/fence.wad;deprecated/free_wad.wad"
// brush 0
{
( -160 -256 80 ) ( -160 64 16 ) ( -160 64 80 ) *swater4 [ 0 -1 0 -16 ] [ 0 0 -1 0 ] 0 6 6
( 192 -432 80 ) ( -64 -432 16 ) ( -64 -432 80 ) *swater4 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 6 6
( 192 64 16 ) ( -64 -256 16 ) ( 192 -256 16 ) *swater4 [ -1 0 0 0 ] [ 0 -1 0 0 ] 0 6 6
( 192 64 80 ) ( -64 -256 80 ) ( -64 64 80 ) *swater4 [ 1 0 0 0 ] [ 0 -1 0 -16 ] 0 6 6
( 192 176 80 ) ( -64 176 16 ) ( 192 176 16 ) *swater4 [ -1 0 0 0 ] [ 0 0 -1 0 ] 0 6 6
( 288 64 80 ) ( 288 -256 16 ) ( 288 -256 80 ) *swater4 [ 0 -1 0 -16 ] [ 0 0 -1 0 ] 0 6 6
}
// brush 1
{
( -176 -256 -16 ) ( -176 -255 -16 ) ( -176 -256 -15 ) tsl_wall1 [ 0 -1 0 0 ] [ 0 0 -1 16 ] 0 6 6
( -176 -432 -16 ) ( -176 -432 -15 ) ( -175 -432 -16 ) tsl_wall1 [ 1 0 0 16 ] [ 0 0 -1 16 ] 0 6 6
( -176 -256 16 ) ( -175 -256 16 ) ( -176 -255 16 ) tsl_wall1 [ -1 0 0 -16 ] [ 0 -1 0 0 ] 0 6 6
( -160 192 272 ) ( -160 193 272 ) ( -159 192 272 ) tsl_wall1 [ 1 0 0 16 ] [ 0 -1 0 0 ] 0 6 6
( -160 176 0 ) ( -159 176 0 ) ( -160 176 1 ) tsl_wall1 [ -1 0 0 -16 ] [ 0 0 -1 16 ] 0 6 6
( -160 192 0 ) ( -160 192 1 ) ( -160 193 0 ) tsl_wall1 [ 0 1 0 0 ] [ 0 0 -1 16 ] 0 6 6
}
// brush 2
{
( -160 176 8 ) ( -160 177 8 ) ( -160 176 9 ) tsl_wall1 [ 0 1.0000000000000002 0 -32 ] [ 0 0 -1.0000000000000002 16 ] 0 6 6
( -160 176 8 ) ( -160 176 9 ) ( -159 176 8 ) tsl_wall1 [ -1 0 0 -16 ] [ 0 0 -1 16 ] 0 6 6
( -160 176 16 ) ( -159 176 16 ) ( -160 177 16 ) tsl_wall1 [ -1.0000000000000002 0 0 -16 ] [ 0 1.0000000000000002 0 -40 ] 0 6 6
( 288 192 272 ) ( 288 193 272 ) ( 289 192 272 ) tsl_wall1 [ -1.0000000000000002 0 0 -16 ] [ 0 -1.0000000000000002 0 48 ] 0 6 6
( 288 192 16 ) ( 289 192 16 ) ( 288 192 17 ) tsl_wall1 [ -1 0 0 -16 ] [ 0 0 -1 16 ] 0 6 6
( 288 192 16 ) ( 288 192 17 ) ( 288 193 16 ) tsl_wall1 [ 0 -1.0000000000000002 0 0 ] [ 0 0 -1.0000000000000002 16 ] 0 6 6
}
// brush 3
{
( -160 -112 16 ) ( -160 -111 16 ) ( -160 -112 17 ) orangestuff8 [ 0 1 0 -16 ] [ 0 0 -1 -16 ] 0 6 6
( -80 -432 0 ) ( -81 -432 0 ) ( -80 -432 1 ) orangestuff8 [ -1 0 0 16 ] [ 0 0 -1 -16 ] 180 6 6
( -80 -432 0 ) ( -80 -431 0 ) ( -81 -432 0 ) orangestuff8 [ 1 0 0 -16 ] [ 0 -1 0 16 ] 180 6 6
( -160 -112 16 ) ( -161 -112 16 ) ( -160 -111 16 ) orangestuff8 [ -1 0 0 16 ] [ 0 -1 0 16 ] 180 6 6
( -160 176 16 ) ( -160 176 17 ) ( -161 176 16 ) orangestuff8 [ 1 0 0 -16 ] [ 0 0 -1 -16 ] 180 6 6
( 288 -432 0 ) ( 288 -432 1 ) ( 288 -431 0 ) orangestuff8 [ 0 -1 0 16 ] [ 0 0 -1 -16 ] 0 6 6
}
// brush 4
{
( -160 -448 8 ) ( -160 -447 8 ) ( -160 -448 9 ) tsl_wall1 [ 0 1.0000000000000002 0 80 ] [ 0 0 -1.0000000000000002 16 ] 0 6 6
( -160 -448 8 ) ( -160 -448 9 ) ( -159 -448 8 ) tsl_wall1 [ -1 0 0 -16 ] [ 0 0 -1 16 ] 0 6 6
( -160 -448 16 ) ( -159 -448 16 ) ( -160 -447 16 ) tsl_wall1 [ -1.0000000000000002 0 0 -16 ] [ 0 1.0000000000000002 0 72 ] 0 6 6
( 288 -432 272 ) ( 288 -431 272 ) ( 289 -432 272 ) tsl_wall1 [ -1.0000000000000002 0 0 -16 ] [ 0 -1.0000000000000002 0 -64 ] 0 6 6
( 288 -432 16 ) ( 289 -432 16 ) ( 288 -432 17 ) tsl_wall1 [ -1 0 0 -16 ] [ 0 0 -1 16 ] 0 6 6
( 288 -432 16 ) ( 288 -432 17 ) ( 288 -431 16 ) tsl_wall1 [ 0 -1.0000000000000002 0 -112 ] [ 0 0 -1.0000000000000002 16 ] 0 6 6
}
// brush 5
{
( -160 -256 272 ) ( -160 -255 272 ) ( -160 -256 273 ) orangestuff8 [ 0 0 -1.0000000000000002 -16 ] [ 0 -1.0000000000000002 0 0 ] 180 6 6
( 288 -432 280 ) ( 288 -432 281 ) ( 289 -432 280 ) orangestuff8 [ -1.0000000000000002 0 0 0 ] [ 0 0 1.0000000000000002 0 ] 180 6 6
( -160 -256 272 ) ( -159 -256 272 ) ( -160 -255 272 ) orangestuff8 [ -1 0 0 0 ] [ 0 -1 0 0 ] 180 6 6
( 288 176 288 ) ( 288 177 288 ) ( 289 176 288 ) orangestuff8 [ -1 0 0 0 ] [ 0 -1 0 0 ] 180 6 6
( 288 176 280 ) ( 289 176 280 ) ( 288 176 281 ) orangestuff8 [ -1.0000000000000002 0 0 0 ] [ 0 0 1.0000000000000002 0 ] 180 6 6
( 288 176 280 ) ( 288 176 281 ) ( 288 177 280 ) orangestuff8 [ 0 0 1.0000000000000002 16 ] [ 0 -1.0000000000000002 0 0 ] 180 6 6
}
// brush 6
{
( 288 192 0 ) ( 288 193 0 ) ( 288 192 1 ) tsl_wall1 [ 0 1 0 0 ] [ 0 0 -1 16 ] 0 6 6
( 304 -432 -16 ) ( 303 -432 -16 ) ( 304 -432 -15 ) tsl_wall1 [ -1 0 0 16 ] [ 0 0 -1 16 ] 180 6 6
( 304 -256 16 ) ( 304 -255 16 ) ( 303 -256 16 ) tsl_wall1 [ 1 0 0 -16 ] [ 0 -1 0 0 ] 180 6 6
( 304 -256 272 ) ( 303 -256 272 ) ( 304 -255 272 ) tsl_wall1 [ 1 0 0 -16 ] [ 0 -1 0 0 ] 180 6 6
( 288 176 0 ) ( 288 176 1 ) ( 287 176 0 ) tsl_wall1 [ 1 0 0 -16 ] [ 0 0 -1 16 ] 180 6 6
( 304 -256 -16 ) ( 304 -256 -15 ) ( 304 -255 -16 ) tsl_wall1 [ 0 -1 0 0 ] [ 0 0 -1 16 ] 0 6 6
}
}
// entity 1
{
"classname" "func_detail_illusionary"
"_mirrorinside" "1"
// brush 0
{
( 16 -144 48 ) ( 16 -143 48 ) ( 16 -144 49 ) {trigger [ 0 1.0000000000000002 0 42.666664 ] [ 0 0 -1.0000000000000002 -32 ] 0 6 6
( 16 -104 48 ) ( 16 -104 49 ) ( 17 -104 48 ) {trigger [ -1 0 0 32 ] [ 0 0 -1 -32 ] 0 6 6
( 16 -144 16 ) ( 17 -144 16 ) ( 16 -143 16 ) {trigger [ -1.0000000000000002 0 0 32 ] [ 0 1.0000000000000002 0 -21.333336 ] 0 6 6
( 96 -128 128 ) ( 96 -127 128 ) ( 97 -128 128 ) {trigger [ -1.0000000000000002 0 0 32 ] [ 0 -1.0000000000000002 0 -8 ] 0 6 6
( 96 -80 64 ) ( 97 -80 64 ) ( 96 -80 65 ) {trigger [ -1 0 0 32 ] [ 0 0 -1 -32 ] 0 6 6
( 40 -128 64 ) ( 40 -128 65 ) ( 40 -127 64 ) {trigger [ 0 -1.0000000000000002 0 12.000002 ] [ 0 0 -1.0000000000000002 -32 ] 0 6 6
}
}
// entity 2
{
"classname" "light"
"origin" "72 -136 168"
"light" "3000"
}
// entity 3
{
"classname" "info_player_start"
"origin" "-88 -64 120"
}

View File

@ -608,11 +608,11 @@ static void ClusterFlow(int clusternum, leafbits_t &buffer, mbsp_t *bsp)
bsp->dvis.set_bit_offset(VIS_PVS, clusternum, visofs);
// Set pointers
// TODO: get rid of, we'll copy this data over from dvis
// during conversion
for (i = 1; i < bsp->dleafs.size(); i++) {
if (bsp->dleafs[i].cluster == clusternum) {
bsp->dleafs[i].visofs = visofs;
if (bsp->loadversion->game->id == GAME_QUAKE_II) {
for (i = 1; i < bsp->numleafs; i++) {
if (bsp->dleafs[i].cluster == clusternum) {
bsp->dleafs[i].visofs = leaf->visofs;
}
}
}