q2 and bug fixes
# Conflicts: # include/qbsp/qbsp.hh # include/qbsp/winding.hh # qbsp/brush.cc # qbsp/csg4.cc # qbsp/merge.cc
This commit is contained in:
parent
8cdb9ff6c4
commit
c95a1e2ccb
|
|
@ -8,5 +8,5 @@ set(BSPINFO_SOURCES
|
|||
${COMMON_INCLUDES})
|
||||
|
||||
add_executable(bspinfo ${BSPINFO_SOURCES})
|
||||
target_link_libraries(bspinfo ${CMAKE_THREAD_LIBS_INIT})
|
||||
target_link_libraries(bspinfo ${CMAKE_THREAD_LIBS_INIT} fmt::fmt)
|
||||
install(TARGETS bspinfo RUNTIME DESTINATION bin)
|
||||
|
|
|
|||
|
|
@ -21,6 +21,39 @@
|
|||
#include <common/cmdlib.hh>
|
||||
#include <common/bspfile.hh>
|
||||
|
||||
inline void PrintBSPInfo(const bspdata_t &bsp) {
|
||||
printf("brushes:\n");
|
||||
for (int32_t i = 0; i < bsp.data.q2bsp.numbrushes; i++) {
|
||||
printf(" %i: contents: %i, num sides: %i, first side: %i\n", i, bsp.data.q2bsp.dbrushes[i].contents, bsp.data.q2bsp.dbrushes[i].numsides, bsp.data.q2bsp.dbrushes[i].firstside);
|
||||
}
|
||||
|
||||
printf("brush sides:\n");
|
||||
for (int32_t i = 0; i < bsp.data.q2bsp.numbrushsides; i++) {
|
||||
auto &plane = bsp.data.q2bsp.dplanes[bsp.data.q2bsp.dbrushsides[i].planenum];
|
||||
printf(" %i: { %i: %f %f %f -> %f }\n", i, plane.type, plane.normal[0], plane.normal[1], plane.normal[2], plane.dist);
|
||||
}
|
||||
|
||||
printf("leaves:\n");
|
||||
for (int32_t i = 0; i < bsp.data.q2bsp.numleafs; i++) {
|
||||
auto &leaf = bsp.data.q2bsp.dleafs[i];
|
||||
|
||||
printf(" %i: contents %i, leafbrushes first %i -> count %i\n", i, leaf.contents, leaf.firstleafbrush, leaf.numleafbrushes);
|
||||
}
|
||||
|
||||
printf("nodes:\n");
|
||||
for (int32_t i = 0; i < bsp.data.q2bsp.numnodes; i++) {
|
||||
auto &node = bsp.data.q2bsp.dnodes[i];
|
||||
auto &plane = bsp.data.q2bsp.dplanes[node.planenum];
|
||||
printf(" %i: { %i: %f %f %f -> %f }\n", i, plane.type, plane.normal[0], plane.normal[1], plane.normal[2], plane.dist);
|
||||
}
|
||||
|
||||
printf("models:\n");
|
||||
for (int32_t i = 0; i < bsp.data.q2bsp.nummodels; i++) {
|
||||
auto &model = bsp.data.q2bsp.dmodels[i];
|
||||
printf(" %i: headnode %i (%f %f %f -> %f %f %f)\n", i, model.headnode, model.mins[0], model.mins[1], model.mins[2], model.maxs[0], model.maxs[1], model.maxs[2]);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
|
|
@ -43,6 +76,8 @@ main(int argc, char **argv)
|
|||
LoadBSPFile(source, &bsp);
|
||||
PrintBSPFileSizes(&bsp);
|
||||
|
||||
PrintBSPInfo(bsp);
|
||||
|
||||
printf("---------------------\n");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -22,6 +22,8 @@
|
|||
#include <common/bspfile.hh>
|
||||
#include <cstdint>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
struct gamedef_generic_t : public gamedef_t {
|
||||
gamedef_generic_t()
|
||||
{
|
||||
|
|
@ -72,6 +74,14 @@ struct gamedef_generic_t : public gamedef_t {
|
|||
bool contents_are_valid(const contentflags_t &, bool) const {
|
||||
throw std::bad_cast();
|
||||
}
|
||||
|
||||
bool portal_can_see_through(const contentflags_t &, const contentflags_t &) const {
|
||||
throw std::bad_cast();
|
||||
}
|
||||
|
||||
std::string get_contents_display(const contentflags_t &contents) const {
|
||||
throw std::bad_cast();
|
||||
}
|
||||
};
|
||||
|
||||
template<gameid_t id>
|
||||
|
|
@ -168,6 +178,33 @@ struct gamedef_q1_like_t : public gamedef_t {
|
|||
bool contents_are_valid(const contentflags_t &contents, bool strict) const {
|
||||
return contents.native <= 0;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
std::string get_contents_display(const contentflags_t &contents) const {
|
||||
switch (contents.native) {
|
||||
case 0:
|
||||
return "UNSET";
|
||||
case CONTENTS_EMPTY:
|
||||
return "EMPTY";
|
||||
case CONTENTS_SOLID:
|
||||
return "SOLID";
|
||||
case CONTENTS_SKY:
|
||||
return "SKY";
|
||||
case CONTENTS_WATER:
|
||||
return "WATER";
|
||||
case CONTENTS_SLIME:
|
||||
return "SLIME";
|
||||
case CONTENTS_LAVA:
|
||||
return "LAVA";
|
||||
default:
|
||||
return fmt::to_string(contents.native);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct gamedef_h2_t : public gamedef_q1_like_t<GAME_HEXEN_II> {
|
||||
|
|
@ -278,10 +315,101 @@ struct gamedef_q2_t : public gamedef_t {
|
|||
}
|
||||
|
||||
bool contents_are_valid(const contentflags_t &contents, bool strict) const {
|
||||
if (!strict) {
|
||||
return true;
|
||||
// check that we don't have more than one visible contents type
|
||||
const int32_t x = (contents.native & ((Q2_LAST_VISIBLE_CONTENTS << 1) - 1));
|
||||
if ((x & (x - 1)) != 0) {
|
||||
return false;
|
||||
}
|
||||
return !contents_are_empty(contents);
|
||||
|
||||
// TODO: check other invalid mixes
|
||||
if (!x && strict) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
constexpr int32_t visible_contents(const int32_t &contents) const {
|
||||
for (int32_t i = 1; i <= Q2_LAST_VISIBLE_CONTENTS; i <<= 1)
|
||||
if (contents & i )
|
||||
return i;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool portal_can_see_through(const contentflags_t &contents0, const contentflags_t &contents1) const {
|
||||
int32_t c0 = contents0.native, c1 = contents1.native;
|
||||
|
||||
if (!visible_contents(c0 ^ c1))
|
||||
return true;
|
||||
|
||||
if ((c0 & Q2_CONTENTS_TRANSLUCENT) ||
|
||||
contents0.is_detail())
|
||||
c0 = 0;
|
||||
if ((c1 & Q2_CONTENTS_TRANSLUCENT) ||
|
||||
contents1.is_detail())
|
||||
c1 = 0;
|
||||
|
||||
// can't see through solid
|
||||
if ((c0 | c1) & Q2_CONTENTS_SOLID)
|
||||
return false;
|
||||
|
||||
// identical on both sides
|
||||
if (!(c0 ^ c1))
|
||||
return true;
|
||||
|
||||
return visible_contents(c0 ^ c1);
|
||||
}
|
||||
|
||||
std::string get_contents_display(const contentflags_t &contents) const {
|
||||
constexpr const char *bitflag_names[] = {
|
||||
"SOLID",
|
||||
"WINDOW",
|
||||
"AUX",
|
||||
"LAVA",
|
||||
"SLIME",
|
||||
"WATER",
|
||||
"MIST",
|
||||
"128",
|
||||
"256",
|
||||
"512",
|
||||
"1024",
|
||||
"2048",
|
||||
"4096",
|
||||
"8192",
|
||||
"16384",
|
||||
"AREAPORTAL",
|
||||
"PLAYERCLIP",
|
||||
"MONSTERCLIP",
|
||||
"CURRENT_0",
|
||||
"CURRENT_90",
|
||||
"CURRENT_180",
|
||||
"CURRENT_270",
|
||||
"CURRENT_UP",
|
||||
"CURRENT_DOWN",
|
||||
"ORIGIN",
|
||||
"MONSTER",
|
||||
"DEADMONSTER",
|
||||
"DETAIL",
|
||||
"TRANSLUCENT",
|
||||
"LADDER",
|
||||
"1073741824",
|
||||
"2147483648"
|
||||
};
|
||||
|
||||
std::string s;
|
||||
|
||||
for (int32_t i = 0; i < std::size(bitflag_names); i++) {
|
||||
if (contents.native & (1 << i)) {
|
||||
if (s.size()) {
|
||||
s += " | " + std::string(bitflag_names[i]);
|
||||
} else {
|
||||
s += bitflag_names[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -331,6 +459,11 @@ bool contentflags_t::is_valid(const gamedef_t *game, bool strict) const {
|
|||
return game->contents_are_valid(*this, strict);
|
||||
}
|
||||
|
||||
std::string contentflags_t::to_string(const gamedef_t *game) const {
|
||||
std::string s = game->get_contents_display(*this);
|
||||
return s;
|
||||
}
|
||||
|
||||
static const char *
|
||||
BSPVersionString(const bspversion_t *version)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -344,6 +344,8 @@ struct contentflags_t {
|
|||
bool types_equal(const contentflags_t &other, const gamedef_t *game) const;
|
||||
|
||||
int32_t priority(const gamedef_t *game) const;
|
||||
|
||||
std::string to_string(const gamedef_t *game) const;
|
||||
};
|
||||
|
||||
struct bsp29_dnode_t {
|
||||
|
|
@ -1106,6 +1108,8 @@ struct gamedef_t
|
|||
}
|
||||
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;
|
||||
virtual bool portal_can_see_through(const contentflags_t &contents0, const contentflags_t &contents1) const = 0;
|
||||
virtual std::string get_contents_display(const contentflags_t &contents) const = 0;
|
||||
};
|
||||
|
||||
// BSP version struct & instances
|
||||
|
|
|
|||
|
|
@ -214,10 +214,10 @@ struct quark_tx_info_t {
|
|||
std::optional<extended_texinfo_t> info;
|
||||
};
|
||||
|
||||
int FindMiptex(const char *name, std::optional<extended_texinfo_t> &extended_info);
|
||||
inline int FindMiptex(const char *name) {
|
||||
int FindMiptex(const char *name, std::optional<extended_texinfo_t> &extended_info, bool internal = false);
|
||||
inline int FindMiptex(const char *name, bool internal = false) {
|
||||
std::optional<extended_texinfo_t> extended_info;
|
||||
return FindMiptex(name, extended_info);
|
||||
return FindMiptex(name, extended_info, internal);
|
||||
}
|
||||
int FindTexinfo(const mtexinfo_t &texinfo);
|
||||
|
||||
|
|
|
|||
|
|
@ -170,7 +170,7 @@ typedef struct visfacet_s {
|
|||
struct visfacet_s *original; // face on node
|
||||
int outputnumber; // only valid for original faces after
|
||||
// write surfaces
|
||||
bool touchesOccupiedLeaf; // internal use in outside.cc
|
||||
bool touchesOccupiedLeaf; // internal use in outside.cc
|
||||
vec3_t origin;
|
||||
vec_t radius;
|
||||
|
||||
|
|
@ -272,6 +272,7 @@ public:
|
|||
bool fbspx_brushes;
|
||||
bool fNoTextures;
|
||||
const bspversion_t *target_version = &bspver_q1;
|
||||
const gamedef_t *target_game = target_version->game;
|
||||
int dxSubdivide;
|
||||
int dxLeakDist;
|
||||
int maxNodeSize;
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ if (embree_FOUND)
|
|||
endif(embree_FOUND)
|
||||
|
||||
add_executable(light ${LIGHT_SOURCES} main.cc)
|
||||
target_link_libraries (light PRIVATE ${CMAKE_THREAD_LIBS_INIT})
|
||||
target_link_libraries (light PRIVATE ${CMAKE_THREAD_LIBS_INIT} fmt::fmt)
|
||||
|
||||
if (embree_FOUND)
|
||||
target_link_libraries (light PRIVATE embree)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
add_definitions(-DDOUBLEVEC_T)
|
||||
|
||||
add_executable(qbsp ${QBSP_SOURCES} main.cc)
|
||||
target_link_libraries(qbsp ${CMAKE_THREAD_LIBS_INIT} TBB::tbb)
|
||||
target_link_libraries(qbsp ${CMAKE_THREAD_LIBS_INIT} TBB::tbb fmt::fmt)
|
||||
install(TARGETS qbsp RUNTIME DESTINATION bin)
|
||||
|
||||
# test (copied from light/CMakeLists.txt)
|
||||
|
|
@ -14,4 +14,4 @@ set(QBSP_TEST_SOURCE
|
|||
add_executable(testqbsp EXCLUDE_FROM_ALL ${QBSP_TEST_SOURCE})
|
||||
add_test(testqbsp testqbsp)
|
||||
|
||||
target_link_libraries (testqbsp ${CMAKE_THREAD_LIBS_INIT} TBB::tbb gtest)
|
||||
target_link_libraries (testqbsp ${CMAKE_THREAD_LIBS_INIT} TBB::tbb gtest fmt::fmt)
|
||||
|
|
|
|||
108
qbsp/brush.cc
108
qbsp/brush.cc
|
|
@ -169,13 +169,11 @@ AddToBounds(mapentity_t *entity, const vec3_t point)
|
|||
//===========================================================================
|
||||
|
||||
static int
|
||||
NormalizePlane(qbsp_plane_t *p)
|
||||
NormalizePlane(qbsp_plane_t *p, bool flip = true)
|
||||
{
|
||||
int i;
|
||||
vec_t ax, ay, az;
|
||||
|
||||
p->outputplanenum = PLANENUM_LEAF;
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
if (p->normal[i] == 1.0) {
|
||||
p->normal[(i + 1) % 3] = 0;
|
||||
|
|
@ -184,10 +182,12 @@ NormalizePlane(qbsp_plane_t *p)
|
|||
return 0; /* no flip */
|
||||
}
|
||||
if (p->normal[i] == -1.0) {
|
||||
if (flip) {
|
||||
p->normal[i] = 1.0;
|
||||
p->dist = -p->dist;
|
||||
}
|
||||
p->normal[(i + 1) % 3] = 0;
|
||||
p->normal[(i + 2) % 3] = 0;
|
||||
p->dist = -p->dist;
|
||||
p->type = PLANE_X + i;
|
||||
return 1; /* plane flipped */
|
||||
}
|
||||
|
|
@ -204,7 +204,7 @@ NormalizePlane(qbsp_plane_t *p)
|
|||
else
|
||||
p->type = PLANE_ANYZ;
|
||||
|
||||
if (p->normal[p->type - PLANE_ANYX] < 0) {
|
||||
if (flip && p->normal[p->type - PLANE_ANYX] < 0) {
|
||||
VectorSubtract(vec3_origin, p->normal, p->normal);
|
||||
p->dist = -p->dist;
|
||||
return 1; /* plane flipped */
|
||||
|
|
@ -262,7 +262,13 @@ NewPlane(const vec3_t normal, const vec_t dist, int *side)
|
|||
qbsp_plane_t plane;
|
||||
VectorCopy(normal, plane.normal);
|
||||
plane.dist = dist;
|
||||
*side = NormalizePlane(&plane) ? SIDE_BACK : SIDE_FRONT;
|
||||
plane.outputplanenum = PLANENUM_LEAF;
|
||||
|
||||
int32_t out_side = NormalizePlane(&plane, side != nullptr);
|
||||
|
||||
if (side) {
|
||||
*side = out_side;
|
||||
}
|
||||
|
||||
int index = map.planes.size();
|
||||
map.planes.push_back(plane);
|
||||
|
|
@ -273,6 +279,7 @@ NewPlane(const vec3_t normal, const vec_t dist, int *side)
|
|||
/*
|
||||
* FindPlane
|
||||
* - Returns a global plane number and the side that will be the front
|
||||
* - if `side` is null, only an exact match will be fetched.
|
||||
*/
|
||||
int
|
||||
FindPlane(const vec3_t normal, const vec_t dist, int *side)
|
||||
|
|
@ -284,9 +291,11 @@ FindPlane(const vec3_t normal, const vec_t dist, int *side)
|
|||
for (int i : map.planehash[plane_hash_fn(&plane)]) {
|
||||
const qbsp_plane_t &p = map.planes.at(i);
|
||||
if (PlaneEqual(&p, &plane)) {
|
||||
if (side) {
|
||||
*side = SIDE_FRONT;
|
||||
}
|
||||
return i;
|
||||
} else if (PlaneInvEqual(&p, &plane)) {
|
||||
} else if (side && PlaneInvEqual(&p, &plane)) {
|
||||
*side = SIDE_BACK;
|
||||
return i;
|
||||
}
|
||||
|
|
@ -394,11 +403,11 @@ 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;
|
||||
auto DiscardHintSkipFace = (options.target_game->id == GAME_QUAKE_II) ? DiscardHintSkipFace_Q2 : DiscardHintSkipFace_Q1;
|
||||
|
||||
mapface = hullbrush->faces;
|
||||
for (i = 0; i < hullbrush->numfaces; i++, mapface++) {
|
||||
if (!hullnum && hullbrush->contents.is_hint()) {
|
||||
if (hullnum <= 0 && hullbrush->contents.is_hint()) {
|
||||
/* Don't generate hintskip faces */
|
||||
const mtexinfo_t &texinfo = map.mtexinfos.at(mapface->texinfo);
|
||||
|
||||
|
|
@ -499,7 +508,7 @@ CreateBrushFaces(const mapentity_t *src, hullbrush_t *hullbrush,
|
|||
(rotate_offset[0] != 0.0 || rotate_offset[1] != 0.0 || rotate_offset[2] != 0.0)
|
||||
&& rottype == rotation_t::hipnotic
|
||||
&& (hullnum >= 0) // hullnum < 0 corresponds to -wrbrushes clipping hulls
|
||||
&& options.target_version->game->id != GAME_HEXEN_II; // never do this in Hexen 2
|
||||
&& options.target_game->id != GAME_HEXEN_II; // never do this in Hexen 2
|
||||
|
||||
if (shouldExpand) {
|
||||
vec_t delta;
|
||||
|
|
@ -849,20 +858,20 @@ Brush_IsDetail(const mapbrush_t *mapbrush)
|
|||
// 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));
|
||||
flags = flags.merge(options.target_game->create_empty_contents(CFLAGS_ORIGIN));
|
||||
else if (!Q_strcasecmp(texname, "hint"))
|
||||
flags = flags.merge(options.target_version->game->create_empty_contents(CFLAGS_HINT));
|
||||
flags = flags.merge(options.target_game->create_empty_contents(CFLAGS_HINT));
|
||||
else if (!Q_strcasecmp(texname, "clip"))
|
||||
flags = flags.merge(options.target_version->game->create_solid_contents(CFLAGS_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_version->game->create_liquid_contents(CONTENTS_LAVA));
|
||||
flags = flags.merge(options.target_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));
|
||||
flags = flags.merge(options.target_game->create_liquid_contents(CONTENTS_SLIME));
|
||||
flags = flags.merge(options.target_game->create_liquid_contents(CONTENTS_WATER));
|
||||
}
|
||||
else if (!Q_strncasecmp(texname, "sky", 3))
|
||||
flags = flags.merge(options.target_version->game->create_sky_contents());
|
||||
flags = flags.merge(options.target_game->create_sky_contents());
|
||||
else
|
||||
return false;
|
||||
|
||||
|
|
@ -888,7 +897,7 @@ Brush_GetContents_Q1(const mapbrush_t *mapbrush, const contentflags_t &base_cont
|
|||
}
|
||||
|
||||
//and anything else is assumed to be a regular solid.
|
||||
return options.target_version->game->create_solid_contents();
|
||||
return options.target_game->create_solid_contents();
|
||||
}
|
||||
|
||||
static contentflags_t
|
||||
|
|
@ -903,6 +912,10 @@ Brush_GetContents_Q2 (const mapbrush_t *mapbrush, const contentflags_t &base_con
|
|||
const mapface_t &mapface = mapbrush->face(i);
|
||||
const mtexinfo_t &texinfo = map.mtexinfos.at(mapface.texinfo);
|
||||
|
||||
if (texinfo.flags.extended & TEX_EXFLAG_SKIP) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!is_trans && (texinfo.flags.native & (Q2_SURF_TRANS33 | Q2_SURF_TRANS66))) {
|
||||
is_trans = true;
|
||||
}
|
||||
|
|
@ -912,7 +925,8 @@ Brush_GetContents_Q2 (const mapbrush_t *mapbrush, const contentflags_t &base_con
|
|||
}
|
||||
|
||||
if (mapface.contents != contents.native) {
|
||||
logprint("mixed face contents\n"); // TODO: need entity # and brush #
|
||||
logprint("mixed face contents (%s != %s at line %i)\n", contentflags_t { mapface.contents }.to_string(options.target_game).c_str(),
|
||||
contents.to_string(options.target_game).c_str(), mapface.linenum); // TODO: need entity # and brush #
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -920,7 +934,10 @@ Brush_GetContents_Q2 (const mapbrush_t *mapbrush, const contentflags_t &base_con
|
|||
// 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);
|
||||
contents.native |= Q2_CONTENTS_TRANSLUCENT;
|
||||
if (contents.native & Q2_CONTENTS_SOLID) {
|
||||
contents.native = (contents.native & ~Q2_CONTENTS_SOLID) | Q2_CONTENTS_WINDOW;
|
||||
}
|
||||
}
|
||||
|
||||
// add extended flags that we may need
|
||||
|
|
@ -936,10 +953,25 @@ Brush_GetContents_Q2 (const mapbrush_t *mapbrush, const contentflags_t &base_con
|
|||
contents.extended |= CFLAGS_ORIGIN;
|
||||
}
|
||||
|
||||
if (contents.native & Q2_CONTENTS_MIST) {
|
||||
contents.extended |= CFLAGS_DETAIL_ILLUSIONARY;
|
||||
}
|
||||
|
||||
if (is_hint) {
|
||||
contents.extended |= CFLAGS_HINT;
|
||||
}
|
||||
|
||||
// FIXME: this is a bit of a hack, but this is because clip
|
||||
// and liquids and stuff are already handled *like* detail by
|
||||
// the compiler.
|
||||
if (contents.extended & CFLAGS_DETAIL) {
|
||||
if (!(contents.native & Q2_CONTENTS_SOLID)) {
|
||||
contents.extended &= ~CFLAGS_DETAIL;
|
||||
}
|
||||
}
|
||||
|
||||
Q_assert(contents.is_valid(options.target_game, false));
|
||||
|
||||
return contents;
|
||||
}
|
||||
|
||||
|
|
@ -985,7 +1017,7 @@ brush_t *LoadBrush(const mapentity_t *src, const mapbrush_t *mapbrush, const con
|
|||
return NULL;
|
||||
}
|
||||
|
||||
if (options.target_version == &bspver_hl)
|
||||
if (options.target_game->id == GAME_HALF_LIFE)
|
||||
{
|
||||
if (hullnum == 1) {
|
||||
vec3_t size[2] = { {-16, -16, -36}, {16, 16, 36} };
|
||||
|
|
@ -1006,7 +1038,7 @@ brush_t *LoadBrush(const mapentity_t *src, const mapbrush_t *mapbrush, const con
|
|||
facelist = CreateBrushFaces(src, &hullbrush, rotate_offset, rottype, hullnum);
|
||||
}
|
||||
}
|
||||
else if (options.target_version->game->id == GAME_HEXEN_II)
|
||||
else if (options.target_game->id == GAME_HEXEN_II)
|
||||
{
|
||||
if (hullnum == 1) {
|
||||
vec3_t size[2] = { {-16, -16, -32}, {16, 16, 24} };
|
||||
|
|
@ -1200,14 +1232,14 @@ Brush_LoadEntity(mapentity_t *dst, const mapentity_t *src, const int hullnum)
|
|||
const bool func_illusionary_visblocker =
|
||||
(0 == Q_strcasecmp(classname, "func_illusionary_visblocker"));
|
||||
|
||||
contentflags_t base_contents = options.target_version->game->create_empty_contents();
|
||||
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_version->game->id == GAME_QUAKE_II) ? Brush_GetContents_Q2 : Brush_GetContents_Q1;
|
||||
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);
|
||||
|
|
@ -1314,19 +1346,19 @@ 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.is_solid(options.target_version->game)) {
|
||||
if (hullnum <= 0 && contents.is_solid(options.target_game)) {
|
||||
if (detail) {
|
||||
contents.extended |= CFLAGS_DETAIL;
|
||||
} else if (detail_illusionary) {
|
||||
contents = contents.merge(options.target_version->game->create_empty_contents(CFLAGS_DETAIL_ILLUSIONARY));
|
||||
contents = contents.merge(options.target_game->create_empty_contents(CFLAGS_DETAIL_ILLUSIONARY));
|
||||
} else if (detail_fence) {
|
||||
contents = contents.merge(options.target_version->game->create_empty_contents(CFLAGS_DETAIL_FENCE)); // fences need to generate leaves
|
||||
contents = contents.merge(options.target_game->create_empty_contents(CFLAGS_DETAIL_FENCE)); // fences need to generate leaves
|
||||
}
|
||||
}
|
||||
|
||||
/* func_detail_illusionary don't exist in the collision hull
|
||||
* (or bspx export) */
|
||||
if (hullnum && detail_illusionary) {
|
||||
if ((options.target_game->id != GAME_QUAKE_II && hullnum) && detail_illusionary) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -1349,14 +1381,14 @@ Brush_LoadEntity(mapentity_t *dst, const mapentity_t *src, const int hullnum)
|
|||
|
||||
/* "hint" brushes don't affect the collision hulls */
|
||||
if (contents.is_hint()) {
|
||||
if (hullnum)
|
||||
if (hullnum > 0)
|
||||
continue;
|
||||
contents = contents.merge(options.target_version->game->create_empty_contents());
|
||||
contents = contents.merge(options.target_game->create_empty_contents());
|
||||
}
|
||||
|
||||
/* entities never use water merging */
|
||||
if (dst != pWorldEnt())
|
||||
contents = contents.merge(options.target_version->game->create_solid_contents());
|
||||
contents = contents.merge(options.target_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.
|
||||
|
|
@ -1366,19 +1398,19 @@ Brush_LoadEntity(mapentity_t *dst, const mapentity_t *src, const int hullnum)
|
|||
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_version->game->create_empty_contents(CFLAGS_DETAIL_FENCE));
|
||||
if (dst != pWorldEnt() && hullnum <= 0 && (contents.extended & CFLAGS_BMODEL_MIRROR_INSIDE)) {
|
||||
contents = contents.merge(options.target_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.is_solid(options.target_version->game) && !contents.is_sky(options.target_version->game))
|
||||
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_version->game))
|
||||
contents = contents.merge(options.target_version->game->create_solid_contents());
|
||||
if (hullnum > 0 && contents.is_sky(options.target_game))
|
||||
contents = contents.merge(options.target_game->create_solid_contents());
|
||||
|
||||
brush_t *brush = LoadBrush(src, mapbrush, contents, rotate_offset, rottype, hullnum);
|
||||
if (!brush)
|
||||
|
|
@ -1396,10 +1428,10 @@ Brush_LoadEntity(mapentity_t *dst, const mapentity_t *src, const int hullnum)
|
|||
} 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_version->game)) {
|
||||
} else if (brush->contents.is_solid(options.target_game)) {
|
||||
brush->next = dst->solid;
|
||||
dst->solid = brush;
|
||||
} else if (brush->contents.is_sky(options.target_version->game)) {
|
||||
} else if (brush->contents.is_sky(options.target_game)) {
|
||||
brush->next = dst->sky;
|
||||
dst->sky = brush;
|
||||
} else {
|
||||
|
|
|
|||
35
qbsp/csg4.cc
35
qbsp/csg4.cc
|
|
@ -55,9 +55,8 @@ MakeSkipTexinfo()
|
|||
|
||||
mtexinfo_t mt { };
|
||||
|
||||
mt.miptex = FindMiptex("skip");
|
||||
mt.miptex = FindMiptex("skip", true);
|
||||
mt.flags = { 0, TEX_EXFLAG_SKIP };
|
||||
memset(&mt.vecs, 0, sizeof(mt.vecs));
|
||||
|
||||
return FindTexinfo(mt);
|
||||
}
|
||||
|
|
@ -374,7 +373,7 @@ SaveFacesToPlaneList(face_t *facelist, bool mirror, std::map<int, face_t *> &pla
|
|||
// 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_version->game))) {
|
||||
|| (options.fContentHack && face->contents[1].is_structural_solid(options.target_game))) {
|
||||
|
||||
// if CFLAGS_BMODEL_MIRROR_INSIDE is set, never change to skip
|
||||
if (!(face->contents[1].extended & CFLAGS_BMODEL_MIRROR_INSIDE)) {
|
||||
|
|
@ -423,7 +422,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_version->game));
|
||||
Q_assert(!clipbrush->contents.is_structural_solid(options.target_game));
|
||||
|
||||
face_t *next;
|
||||
|
||||
|
|
@ -434,7 +433,7 @@ SaveInsideFaces(face_t *face, const brush_t *clipbrush, face_t **savelist)
|
|||
next = face->next;
|
||||
face->contents[0] = clipbrush->contents;
|
||||
|
||||
if (face->contents[1].is_structural_sky_or_solid(options.target_version->game)
|
||||
if (face->contents[1].is_structural_sky_or_solid(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
|
||||
|
|
@ -450,14 +449,14 @@ 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] = options.target_version->game->create_empty_contents(CFLAGS_STRUCTURAL_COVERED_BY_DETAIL);
|
||||
face->contents[0] = options.target_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].is_structural_sky_or_solid(options.target_version->game) &&
|
||||
Q_assert(!(face->contents[1].is_structural_sky_or_solid(options.target_game) &&
|
||||
face->contents[0].is_detail(CFLAGS_DETAIL)));
|
||||
|
||||
/*
|
||||
|
|
@ -467,7 +466,7 @@ SaveInsideFaces(face_t *face, const brush_t *clipbrush, face_t **savelist)
|
|||
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_version->game)) {
|
||||
if (face->contents[1].is_empty(options.target_game)) {
|
||||
face->contents[1] = clipbrush->contents;
|
||||
}
|
||||
|
||||
|
|
@ -533,7 +532,7 @@ CopyBrushFaces(const brush_t *brush)
|
|||
brushfaces++;
|
||||
newface = (face_t *)AllocMem(OTHER, sizeof(face_t), true);
|
||||
*newface = *face;
|
||||
newface->contents[0] = options.target_version->game->create_empty_contents();
|
||||
newface->contents[0] = options.target_game->create_empty_contents();
|
||||
newface->contents[1] = brush->contents;
|
||||
newface->lmshift[0] = brush->lmshift;
|
||||
newface->lmshift[1] = brush->lmshift;
|
||||
|
|
@ -626,7 +625,7 @@ CSGFaces(const mapentity_t *entity)
|
|||
|
||||
// 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_version->game)
|
||||
if (clipbrush->contents.types_equal(brush->contents, options.target_game)
|
||||
&& !clipbrush->contents.clips_same_type()) {
|
||||
/* _noclipfaces key */
|
||||
continue;
|
||||
|
|
@ -668,18 +667,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.is_structural_solid(options.target_version->game) && !clipbrush->contents.is_structural_solid(options.target_version->game))
|
||||
if ((brush->contents.is_structural_solid(options.target_game) && !clipbrush->contents.is_structural_solid(options.target_game))
|
||||
|
||||
|| (brush->contents.is_structural_sky(options.target_version->game) && !clipbrush->contents.is_structural_sky_or_solid(options.target_version->game))
|
||||
|| (brush->contents.is_structural_sky(options.target_game) && !clipbrush->contents.is_structural_sky_or_solid(options.target_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)
|
||||
|| ((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_liquid(options.target_version->game) && clipbrush->contents.is_detail(CFLAGS_DETAIL_ILLUSIONARY))
|
||||
|| (brush->contents.is_liquid(options.target_game) && clipbrush->contents.is_detail(CFLAGS_DETAIL_ILLUSIONARY))
|
||||
|
||||
|| (brush->contents.is_fence() && (clipbrush->contents.is_liquid(options.target_version->game) || clipbrush->contents.is_fence())))
|
||||
|| (brush->contents.is_fence() && (clipbrush->contents.is_liquid(options.target_game) || clipbrush->contents.is_fence())))
|
||||
{
|
||||
SaveInsideFaces(inside, clipbrush, &outside);
|
||||
} else {
|
||||
|
|
@ -701,7 +700,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.is_solid(options.target_version->game);
|
||||
const bool mirror = options.fContentHack ? true : !brush->contents.is_solid(options.target_game);
|
||||
SaveFacesToPlaneList(outside, mirror, planefaces);
|
||||
}
|
||||
surface_t *surfaces = BuildSurfaces(planefaces);
|
||||
|
|
|
|||
63
qbsp/map.cc
63
qbsp/map.cc
|
|
@ -195,19 +195,17 @@ static std::optional<wal_t> LoadWal(const char *name) {
|
|||
}
|
||||
|
||||
int
|
||||
FindMiptex(const char *name, std::optional<extended_texinfo_t> &extended_info)
|
||||
FindMiptex(const char *name, std::optional<extended_texinfo_t> &extended_info, bool internal)
|
||||
{
|
||||
const char *pathsep;
|
||||
int i;
|
||||
|
||||
if (options.target_version->game->id != GAME_QUAKE_II) {
|
||||
if (options.target_game->id != GAME_QUAKE_II) {
|
||||
/* Ignore leading path in texture names (Q2 map compatibility) */
|
||||
pathsep = strrchr(name, '/');
|
||||
if (pathsep)
|
||||
name = pathsep + 1;
|
||||
|
||||
extended_info = extended_texinfo_t { };
|
||||
|
||||
for (i = 0; i < map.nummiptex(); i++) {
|
||||
const texdata_t &tex = map.miptex.at(i);
|
||||
|
||||
|
|
@ -226,18 +224,22 @@ FindMiptex(const char *name, std::optional<extended_texinfo_t> &extended_info)
|
|||
}
|
||||
} else {
|
||||
// load .wal first
|
||||
std::optional<wal_t> wal = LoadWal(name);
|
||||
std::optional<wal_t> wal;
|
||||
|
||||
// FIXME: this spams the console if wal not found, because we
|
||||
// need to load the wal to figure out if it will match anything
|
||||
// in the list...
|
||||
if (!wal.has_value()) {
|
||||
Message(msgLiteral, "Couldn't locate wal for %s\n", name);
|
||||
wal = wal_t {};
|
||||
if (!internal || !extended_info.has_value()) {
|
||||
wal = LoadWal(name);
|
||||
|
||||
if (!wal.has_value()) {
|
||||
//FIXME
|
||||
//Message(msgLiteral, "Couldn't locate wal for %s\n", name);
|
||||
if (!extended_info.has_value()) {
|
||||
extended_info = extended_texinfo_t { };
|
||||
}
|
||||
} else if (!extended_info.has_value()) {
|
||||
extended_info = extended_texinfo_t { wal->contents, wal->flags, wal->value };
|
||||
}
|
||||
}
|
||||
|
||||
extended_info = extended_info.value_or(extended_texinfo_t { wal->contents, wal->flags, wal->value });
|
||||
|
||||
for (i = 0; i < map.nummiptex(); i++) {
|
||||
const texdata_t &tex = map.miptex.at(i);
|
||||
|
||||
|
|
@ -250,10 +252,10 @@ FindMiptex(const char *name, std::optional<extended_texinfo_t> &extended_info)
|
|||
}
|
||||
|
||||
i = map.miptex.size();
|
||||
map.miptex.push_back({ name, wal->flags, wal->value });
|
||||
map.miptex.push_back({ name, extended_info->flags, extended_info->value });
|
||||
|
||||
/* Handle animating textures carefully */
|
||||
if (wal->anim_name[0]) {
|
||||
if (wal && wal->anim_name[0]) {
|
||||
FindMiptex(wal->anim_name);
|
||||
}
|
||||
}
|
||||
|
|
@ -365,7 +367,13 @@ SurfFlagsForEntity(const mtexinfo_t &texinfo, const mapentity_t *entity)
|
|||
const char *texname = map.miptex.at(texinfo.miptex).name.c_str();
|
||||
const int shadow = atoi(ValueForKey(entity, "_shadow"));
|
||||
// These flags are pulled from surf flags in Q2.
|
||||
if (options.target_version->game->id != GAME_QUAKE_II) {
|
||||
// TODO: the Q1 version of this block can now be moved into texinfo
|
||||
// loading by shoving them inside of texinfo.flags like
|
||||
// Q2 does. Similarly, we can move the Q2 block out
|
||||
// into a special function, like.. I dunno,
|
||||
// game->surface_flags_from_name(surfflags_t &inout, const char *name)
|
||||
// which we can just call instead of this block.
|
||||
if (options.target_game->id != GAME_QUAKE_II) {
|
||||
if (IsSkipName(texname))
|
||||
flags.extended |= TEX_EXFLAG_SKIP;
|
||||
if (IsHintName(texname))
|
||||
|
|
@ -375,9 +383,15 @@ SurfFlagsForEntity(const mtexinfo_t &texinfo, const mapentity_t *entity)
|
|||
} else {
|
||||
flags.native = texinfo.flags.native;
|
||||
|
||||
if (texinfo.flags.native & Q2_SURF_NODRAW)
|
||||
// This fixes a bug in some old maps.
|
||||
if ((flags.native & (Q2_SURF_SKY | Q2_SURF_NODRAW)) == (Q2_SURF_SKY | Q2_SURF_NODRAW)) {
|
||||
flags.native &= ~Q2_SURF_NODRAW;
|
||||
//logprint("Corrected invalid SKY flag\n");
|
||||
}
|
||||
|
||||
if ((flags.native & Q2_SURF_NODRAW) || IsSkipName(texname))
|
||||
flags.extended |= TEX_EXFLAG_SKIP;
|
||||
if (texinfo.flags.native & Q2_SURF_HINT)
|
||||
if ((flags.native & Q2_SURF_HINT) || IsHintName(texname))
|
||||
flags.extended |= TEX_EXFLAG_HINT;
|
||||
}
|
||||
if (IsNoExpandName(texname))
|
||||
|
|
@ -488,7 +502,7 @@ ParseEpair(parser_t *parser, mapentity_t *entity)
|
|||
// Quake II uses multiple starts for level transitions/backtracking.
|
||||
// TODO: instead, this should check targetnames. There should only be
|
||||
// one info_player_start per targetname in Q2.
|
||||
if (options.target_version->game->id != GAME_QUAKE_II && (rgfStartSpots & info_player_start))
|
||||
if (options.target_game->id != GAME_QUAKE_II && (rgfStartSpots & info_player_start))
|
||||
Message(msgWarning, warnMultipleStarts);
|
||||
rgfStartSpots |= info_player_start;
|
||||
} else if (!Q_strcasecmp(epair->value, "info_player_deathmatch")) {
|
||||
|
|
@ -1485,6 +1499,8 @@ ParseTextureDef(parser_t *parser, mapface_t &mapface, const mapbrush_t *brush, m
|
|||
tx->flags = mapface.flags = { extinfo.info->flags };
|
||||
tx->value = mapface.value = extinfo.info->value;
|
||||
|
||||
Q_assert(contentflags_t { mapface.contents }.is_valid(options.target_game, false));
|
||||
|
||||
if (!planepts || !plane)
|
||||
return;
|
||||
|
||||
|
|
@ -1678,11 +1694,12 @@ ParseBrush(parser_t *parser, const mapentity_t *entity)
|
|||
if (face.get() == nullptr)
|
||||
continue;
|
||||
|
||||
if (options.target_version->game->id == GAME_QUAKE_II) {
|
||||
// FIXME: can we move this somewhere later?
|
||||
if (options.target_game->id == GAME_QUAKE_II) {
|
||||
// translucent objects are automatically classified as detail
|
||||
//if ((face->flags.native & (Q2_SURF_TRANS33 | Q2_SURF_TRANS66))
|
||||
// || (face->contents & (Q2_CONTENTS_PLAYERCLIP | Q2_CONTENTS_MONSTERCLIP)))
|
||||
// face->contents |= Q2_CONTENTS_DETAIL;
|
||||
if ((face->flags.native & (Q2_SURF_TRANS33 | Q2_SURF_TRANS66))
|
||||
|| (face->contents & (Q2_CONTENTS_PLAYERCLIP | Q2_CONTENTS_MONSTERCLIP)))
|
||||
face->contents |= Q2_CONTENTS_DETAIL;
|
||||
|
||||
if (!(face->contents & (((Q2_LAST_VISIBLE_CONTENTS << 1)-1)
|
||||
| Q2_CONTENTS_PLAYERCLIP | Q2_CONTENTS_MONSTERCLIP) ) )
|
||||
|
|
|
|||
|
|
@ -338,7 +338,7 @@ ClearOutFaces(node_t *node)
|
|||
}
|
||||
|
||||
// visit the leaf
|
||||
if (!node->contents.is_solid(options.target_version->game)) {
|
||||
if (!node->contents.is_solid(options.target_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.is_sky(options.target_version->game)
|
||||
|| node->contents.is_solid(options.target_version->game))
|
||||
if (node->contents.is_sky(options.target_game)
|
||||
|| node->contents.is_solid(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,
|
||||
|
|
@ -383,7 +383,7 @@ OutLeafsToSolid_r(node_t *node, int *outleafs_count)
|
|||
}
|
||||
|
||||
// Finally, we can fill it in as void.
|
||||
node->contents = options.target_version->game->create_solid_contents();
|
||||
node->contents = options.target_game->create_solid_contents();
|
||||
*outleafs_count += 1;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -59,47 +59,42 @@ ClusterContents(const node_t *node)
|
|||
if (node->planenum == PLANENUM_LEAF)
|
||||
return node->contents;
|
||||
|
||||
return options.target_version->game->cluster_contents(ClusterContents(node->children[0]), ClusterContents(node->children[1]));
|
||||
return options.target_game->cluster_contents(ClusterContents(node->children[0]), ClusterContents(node->children[1]));
|
||||
}
|
||||
|
||||
/*
|
||||
* Return true if possible to see the through the contents of the portals nodes
|
||||
*/
|
||||
/* Return true if possible to see the through the contents of the portals nodes */
|
||||
static bool
|
||||
PortalThru(const portal_t *p)
|
||||
{
|
||||
contentflags_t contents0 = ClusterContents(p->nodes[0]);
|
||||
contentflags_t contents1 = ClusterContents(p->nodes[1]);
|
||||
|
||||
/* Can't see through solids */
|
||||
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 */
|
||||
if ((contents0.extended | contents1.extended) & CFLAGS_ILLUSIONARY_VISBLOCKER)
|
||||
return false;
|
||||
|
||||
/* If contents values are the same and not solid, can see through */
|
||||
if (contents0 == contents1)
|
||||
return true;
|
||||
// FIXME: we can't move this directly to portal_can_see_through because
|
||||
// "options" isn't exposed there.
|
||||
if (options.target_game->id != GAME_QUAKE_II) {
|
||||
/* If water is transparent, liquids are like empty space */
|
||||
if (options.fTranswater) {
|
||||
if (contents0.is_liquid(options.target_game) && contents1.is_empty(options.target_game))
|
||||
return true;
|
||||
if (contents1.is_liquid(options.target_game) && contents0.is_empty(options.target_game))
|
||||
return true;
|
||||
}
|
||||
|
||||
/* If water is transparent, liquids are like empty space */
|
||||
if (options.fTranswater) {
|
||||
if (contents0.is_liquid(options.target_version->game) && contents1.is_empty(options.target_version->game))
|
||||
return true;
|
||||
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.is_sky(options.target_game) && contents1.is_empty(options.target_game))
|
||||
return true;
|
||||
if (contents0.is_empty(options.target_game) && contents1.is_sky(options.target_game))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/* If sky is transparent, then sky is like empty space */
|
||||
if (options.fTranssky) {
|
||||
if (contents0.is_sky(options.target_version->game) && contents1.is_empty(options.target_version->game))
|
||||
return true;
|
||||
if (contents0.is_empty(options.target_version->game) && contents1.is_sky(options.target_version->game))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
// Check per-game visibility
|
||||
return options.target_game->portal_can_see_through(contents0, contents1);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -116,7 +111,7 @@ WritePortals_r(node_t *node, FILE *portalFile, bool clusters)
|
|||
WritePortals_r(node->children[1], portalFile, clusters);
|
||||
return;
|
||||
}
|
||||
if (node->contents.is_solid(options.target_version->game))
|
||||
if (node->contents.is_solid(options.target_game))
|
||||
return;
|
||||
|
||||
for (p = node->portals; p; p = next) {
|
||||
|
|
@ -161,7 +156,7 @@ WriteClusters_r(node_t *node, FILE *portalFile, int viscluster)
|
|||
viscluster = WriteClusters_r(node->children[1], portalFile, viscluster);
|
||||
return viscluster;
|
||||
}
|
||||
if (node->contents.is_solid(options.target_version->game))
|
||||
if (node->contents.is_solid(options.target_game))
|
||||
return viscluster;
|
||||
|
||||
/* If we're in the next cluster, start a new line */
|
||||
|
|
@ -223,7 +218,7 @@ NumberLeafs_r(node_t *node, portal_state_t *state, int cluster)
|
|||
return;
|
||||
}
|
||||
|
||||
if (node->contents.is_solid(options.target_version->game)) {
|
||||
if (node->contents.is_structural_solid(options.target_game)) {
|
||||
/* solid block, viewpoint never inside */
|
||||
node->visleafnum = -1;
|
||||
node->viscluster = -1;
|
||||
|
|
@ -380,7 +375,7 @@ MakeHeadnodePortals(const mapentity_t *entity, node_t *node)
|
|||
}
|
||||
|
||||
outside_node.planenum = PLANENUM_LEAF;
|
||||
outside_node.contents = options.target_version->game->create_solid_contents();
|
||||
outside_node.contents = options.target_game->create_solid_contents();
|
||||
outside_node.portals = NULL;
|
||||
|
||||
for (i = 0; i < 3; i++)
|
||||
|
|
@ -671,7 +666,7 @@ PortalizeWorld(const mapentity_t *entity, node_t *headnode, const int hullnum)
|
|||
MakeHeadnodePortals(entity, headnode);
|
||||
CutNodePortals_r(headnode, &state);
|
||||
|
||||
if (!hullnum) {
|
||||
if (hullnum <= 0) {
|
||||
/* save portal file for vis tracing */
|
||||
WritePortalfile(headnode, &state);
|
||||
|
||||
|
|
|
|||
114
qbsp/qbsp.cc
114
qbsp/qbsp.cc
|
|
@ -20,7 +20,8 @@
|
|||
*/
|
||||
|
||||
#include <memory>
|
||||
#include <string.h>
|
||||
#include <cstring>
|
||||
#include <algorithm>
|
||||
|
||||
#include <common/log.hh>
|
||||
#include <common/aabb.hh>
|
||||
|
|
@ -36,27 +37,38 @@ static const char *IntroString =
|
|||
options_t options;
|
||||
|
||||
bool node_t::opaque() const {
|
||||
return contents.is_structural_sky_or_solid(options.target_version->game);
|
||||
return contents.is_structural_sky_or_solid(options.target_game);
|
||||
}
|
||||
|
||||
static struct {
|
||||
uint32_t total_brushes, total_brush_sides;
|
||||
uint32_t total_leaf_brushes, unique_leaf_brushes;
|
||||
} brush_state;
|
||||
|
||||
static void ExportBrushList_r(const mapentity_t *entity, node_t *node)
|
||||
{
|
||||
if (node->planenum == PLANENUM_LEAF)
|
||||
{
|
||||
if (node->visleafnum == -1) {
|
||||
node->firstleafbrush = map.exported_leafbrushes.size();
|
||||
node->numleafbrushes = 0;
|
||||
|
||||
if (node->contents.native) {
|
||||
int32_t b_id = 0;
|
||||
std::vector<uint32_t> brushes;
|
||||
|
||||
for (const brush_t *b = entity->brushes; b; b = b->next, b_id++)
|
||||
{
|
||||
if (aabb3f(qvec3f(node->mins[0], node->mins[1], node->mins[2]), qvec3f(node->maxs[0], node->maxs[1], node->maxs[2])).intersectWith(
|
||||
aabb3f(qvec3f(b->mins[0], b->mins[1], b->mins[2]), qvec3f(b->maxs[0], b->maxs[1], b->maxs[2]))).valid) {
|
||||
map.exported_leafbrushes.push_back(b_id);
|
||||
node->numleafbrushes++;
|
||||
brushes.push_back(b_id);
|
||||
}
|
||||
}
|
||||
|
||||
node->numleafbrushes = brushes.size();
|
||||
brush_state.total_leaf_brushes += node->numleafbrushes;
|
||||
|
||||
if (brushes.size()) {
|
||||
node->firstleafbrush = map.exported_leafbrushes.size();
|
||||
map.exported_leafbrushes.insert(map.exported_leafbrushes.end(), brushes.begin(), brushes.end());
|
||||
brush_state.unique_leaf_brushes += node->numleafbrushes;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
|
|
@ -68,23 +80,75 @@ static void ExportBrushList_r(const mapentity_t *entity, node_t *node)
|
|||
|
||||
static void ExportBrushList(const mapentity_t *entity, node_t *node)
|
||||
{
|
||||
brush_state = { };
|
||||
|
||||
for (const brush_t *b = entity->brushes; b; b = b->next)
|
||||
{
|
||||
dbrush_t brush { (int32_t) map.exported_brushsides.size(), 0, b->contents.native };
|
||||
|
||||
for (const face_t *f = b->faces; f; f = f->next)
|
||||
{
|
||||
if (map.planes[f->planenum].outputplanenum == -1) {
|
||||
continue;
|
||||
int32_t planenum = f->planenum;
|
||||
int32_t outputplanenum;
|
||||
|
||||
if (f->planeside) {
|
||||
vec3_t flipped;
|
||||
VectorCopy(map.planes[f->planenum].normal, flipped);
|
||||
VectorInverse(flipped);
|
||||
planenum = FindPlane(flipped, -map.planes[f->planenum].dist, nullptr);
|
||||
outputplanenum = ExportMapPlane(planenum);
|
||||
} else {
|
||||
planenum = FindPlane(map.planes[f->planenum].normal, map.planes[f->planenum].dist, nullptr);
|
||||
outputplanenum = ExportMapPlane(planenum);
|
||||
}
|
||||
map.exported_brushsides.push_back({ (uint32_t) map.planes[f->planenum].outputplanenum, map.mtexinfos[f->texinfo].outputnum.value_or(-1) });
|
||||
|
||||
map.exported_brushsides.push_back({ (uint32_t) outputplanenum, map.mtexinfos[f->texinfo].outputnum.value_or(-1) });
|
||||
brush.numsides++;
|
||||
brush_state.total_brush_sides++;
|
||||
}
|
||||
|
||||
// add any axis planes not contained in the brush to bevel off corners
|
||||
for (int32_t x=0 ; x<3 ; x++)
|
||||
for (int32_t s=-1 ; s<=1 ; s+=2)
|
||||
{
|
||||
// add the plane
|
||||
vec3_t normal { };
|
||||
float dist;
|
||||
VectorCopy (vec3_origin, normal);
|
||||
normal[x] = s;
|
||||
if (s == -1)
|
||||
dist = -b->mins[x];
|
||||
else
|
||||
dist = b->maxs[x];
|
||||
int32_t side;
|
||||
int32_t planenum = FindPlane(normal, dist, &side);
|
||||
face_t *f;
|
||||
|
||||
for (f = b->faces; f; f = f->next)
|
||||
if (f->planenum == planenum)
|
||||
break;
|
||||
|
||||
if (f == nullptr)
|
||||
{
|
||||
planenum = FindPlane(normal, dist, nullptr);
|
||||
int32_t outputplanenum = ExportMapPlane(planenum);
|
||||
|
||||
map.exported_brushsides.push_back({ (uint32_t) outputplanenum, map.exported_brushsides[map.exported_brushsides.size() - 1].texinfo });
|
||||
brush.numsides++;
|
||||
brush_state.total_brush_sides++;
|
||||
}
|
||||
}
|
||||
|
||||
map.exported_brushes.push_back(brush);
|
||||
brush_state.total_brushes++;
|
||||
}
|
||||
|
||||
ExportBrushList_r(entity, node);
|
||||
|
||||
Message(msgStat, "%8u total brushes", brush_state.total_brushes);
|
||||
Message(msgStat, "%8u total brush sides", brush_state.total_brush_sides);
|
||||
Message(msgStat, "%8u total leaf brushes", brush_state.total_leaf_brushes);
|
||||
Message(msgStat, "%8u unique leaf brushes (%.2f%%)", brush_state.unique_leaf_brushes, (brush_state.unique_leaf_brushes / (float) brush_state.total_leaf_brushes) * 100);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -126,7 +190,7 @@ ProcessEntity(mapentity_t *entity, const int hullnum)
|
|||
if (options.fVerbose)
|
||||
PrintEntity(entity);
|
||||
|
||||
if (hullnum == 0)
|
||||
if (hullnum <= 0)
|
||||
Message(msgStat, "MODEL: %s", mod);
|
||||
SetKeyValue(entity, "model", mod);
|
||||
}
|
||||
|
|
@ -218,11 +282,11 @@ ProcessEntity(mapentity_t *entity, const int hullnum)
|
|||
*/
|
||||
surfs = CSGFaces(entity);
|
||||
|
||||
if (options.fObjExport && entity == pWorldEnt() && hullnum == 0) {
|
||||
if (options.fObjExport && entity == pWorldEnt() && hullnum <= 0) {
|
||||
ExportObj_Surfaces("post_csg", surfs);
|
||||
}
|
||||
|
||||
if (hullnum != 0) {
|
||||
if (hullnum > 0) {
|
||||
nodes = SolidBSP(entity, surfs, true);
|
||||
if (entity == pWorldEnt() && !options.fNofill) {
|
||||
// assume non-world bmodels are simple
|
||||
|
|
@ -297,11 +361,13 @@ ProcessEntity(mapentity_t *entity, const int hullnum)
|
|||
}
|
||||
|
||||
firstface = MakeFaceEdges(entity, nodes);
|
||||
ExportDrawNodes(entity, nodes, firstface);
|
||||
|
||||
if (options.target_version->game->id == GAME_QUAKE_II) {
|
||||
if (options.target_game->id == GAME_QUAKE_II) {
|
||||
Message(msgProgress, "Calculating Brush List");
|
||||
ExportBrushList(entity, nodes);
|
||||
}
|
||||
|
||||
ExportDrawNodes(entity, nodes, firstface);
|
||||
}
|
||||
|
||||
FreeBrushes(entity);
|
||||
|
|
@ -624,6 +690,12 @@ CreateHulls(void)
|
|||
if (!options.fNoverbose)
|
||||
options.fVerbose = true;
|
||||
|
||||
if (options.target_game->id == GAME_QUAKE_II)
|
||||
{
|
||||
CreateSingleHull(-1);
|
||||
return;
|
||||
}
|
||||
|
||||
CreateSingleHull(0);
|
||||
|
||||
/* ignore the clipping hulls altogether */
|
||||
|
|
@ -633,9 +705,10 @@ CreateHulls(void)
|
|||
CreateSingleHull(1);
|
||||
CreateSingleHull(2);
|
||||
|
||||
if (options.target_version == &bspver_hl)
|
||||
// FIXME: use game->get_hull_count
|
||||
if (options.target_game->id == GAME_HALF_LIFE)
|
||||
CreateSingleHull(3);
|
||||
else if (options.target_version->game->id == GAME_HEXEN_II)
|
||||
else if (options.target_game->id == GAME_HEXEN_II)
|
||||
{ /*note: h2mp doesn't use hull 2 automatically, however gamecode can explicitly set ent.hull=3 to access it*/
|
||||
CreateSingleHull(3);
|
||||
CreateSingleHull(4);
|
||||
|
|
@ -1033,9 +1106,12 @@ ParseOptions(char *szOptions)
|
|||
}
|
||||
|
||||
// force specific flags for Q2
|
||||
if (options.target_version->game->id == GAME_QUAKE_II) {
|
||||
if (options.target_game->id == GAME_QUAKE_II) {
|
||||
options.fNoclip = true;
|
||||
}
|
||||
|
||||
// update target game
|
||||
options.target_game = options.target_version->game;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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.is_structural_solid(options.target_version->game)
|
||||
&& node->children[1]->contents.is_structural_solid(options.target_version->game)) {
|
||||
if (node->children[0]->contents.is_structural_solid(options.target_game)
|
||||
&& node->children[1]->contents.is_structural_solid(options.target_game)) {
|
||||
// This discards any faces on-node. Should be safe (?)
|
||||
ConvertNodeToLeaf(node, options.target_version->game->create_solid_contents());
|
||||
ConvertNodeToLeaf(node, options.target_game->create_solid_contents());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -538,8 +538,9 @@ CalcSurfaceInfo(surface_t *surf)
|
|||
surf->has_struct = false;
|
||||
|
||||
for (const face_t *f = surf->faces; f; f = f->next) {
|
||||
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__);
|
||||
for (auto &contents : f->contents)
|
||||
if (!contents.is_valid(options.target_game, false))
|
||||
Error("Bad contents in face: %s (%s)", contents.to_string(options.target_game).c_str(), __func__);
|
||||
|
||||
surf->lmshift = (f->lmshift[0]<f->lmshift[1])?f->lmshift[0]:f->lmshift[1];
|
||||
|
||||
|
|
@ -751,19 +752,19 @@ LinkConvexFaces(surface_t *planelist, node_t *leafnode)
|
|||
for (face_t *f = surf->faces; f; f = f->next) {
|
||||
count++;
|
||||
|
||||
const int currentpri = contents.has_value() ? contents->priority(options.target_version->game) : -1;
|
||||
const int fpri = f->contents[0].priority(options.target_version->game);
|
||||
const int currentpri = contents.has_value() ? contents->priority(options.target_game) : -1;
|
||||
const int fpri = f->contents[0].priority(options.target_game);
|
||||
if (fpri > currentpri) {
|
||||
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].is_empty(options.target_version->game));
|
||||
Q_assert(f->contents[0].is_empty(options.target_game));
|
||||
|
||||
const contentflags_t solid_detail = options.target_version->game->create_solid_contents(CFLAGS_DETAIL);
|
||||
const contentflags_t solid_detail = options.target_game->create_solid_contents(CFLAGS_DETAIL);
|
||||
|
||||
if (solid_detail.priority(options.target_version->game) > currentpri) {
|
||||
if (solid_detail.priority(options.target_game) > currentpri) {
|
||||
contents = solid_detail;
|
||||
}
|
||||
}
|
||||
|
|
@ -773,7 +774,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.
|
||||
leafnode->contents = contents.value_or(options.target_version->game->create_solid_contents()); // FIXME: Need to create CONTENTS_DETAIL sometimes?
|
||||
leafnode->contents = contents.value_or(options.target_game->create_solid_contents()); // FIXME: Need to create CONTENTS_DETAIL sometimes?
|
||||
|
||||
if (leafnode->contents.extended & CFLAGS_ILLUSIONARY_VISBLOCKER) {
|
||||
c_illusionary_visblocker++;
|
||||
|
|
@ -783,14 +784,14 @@ LinkConvexFaces(surface_t *planelist, node_t *leafnode)
|
|||
c_detail_illusionary++;
|
||||
} else if (leafnode->contents.extended & CFLAGS_DETAIL) {
|
||||
c_detail++;
|
||||
} else if (leafnode->contents.is_empty(options.target_version->game)) {
|
||||
} else if (leafnode->contents.is_empty(options.target_game)) {
|
||||
c_empty++;
|
||||
} else if (leafnode->contents.is_solid(options.target_version->game)) {
|
||||
} else if (leafnode->contents.is_solid(options.target_game)) {
|
||||
c_solid++;
|
||||
} else if (leafnode->contents.is_liquid(options.target_version->game) || leafnode->contents.is_sky(options.target_version->game)) {
|
||||
} else if (leafnode->contents.is_liquid(options.target_game) || leafnode->contents.is_sky(options.target_game)) {
|
||||
c_water++;
|
||||
} else {
|
||||
Error("Bad contents in face (%s)", __func__);
|
||||
//Error("Bad contents in face: %s (%s)", leafnode->contents.to_string(options.target_game).c_str(), __func__);
|
||||
}
|
||||
|
||||
// write the list of the original faces to the leaf's markfaces
|
||||
|
|
@ -949,11 +950,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 = options.target_version->game->create_empty_contents();
|
||||
headnode->children[0]->contents = options.target_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 = options.target_version->game->create_empty_contents();
|
||||
headnode->children[1]->contents = options.target_game->create_empty_contents();
|
||||
headnode->children[1]->markfaces = (face_t **)AllocMem(OTHER, sizeof(face_t *), true);
|
||||
|
||||
return headnode;
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ SubdivideFace(face_t *f, face_t **prevptr)
|
|||
{
|
||||
vec_t mins, maxs;
|
||||
vec_t v;
|
||||
int axis, i;
|
||||
int axis;
|
||||
qbsp_plane_t plane;
|
||||
face_t *front, *back, *next;
|
||||
const mtexinfo_t *tex;
|
||||
|
|
@ -49,7 +49,7 @@ SubdivideFace(face_t *f, face_t **prevptr)
|
|||
tex = &map.mtexinfos.at(f->texinfo);
|
||||
|
||||
if (tex->flags.extended & (TEX_EXFLAG_SKIP | TEX_EXFLAG_HINT) ||
|
||||
!options.target_version->game->surf_is_subdivided(tex->flags))
|
||||
!options.target_game->surf_is_subdivided(tex->flags))
|
||||
return;
|
||||
//subdivision is pretty much pointless other than because of lightmap block limits
|
||||
//one lightmap block will always be added at the end, for smooth interpolation
|
||||
|
|
@ -292,7 +292,7 @@ GetEdge(mapentity_t *entity, const vec3_t p1, const vec3_t p2,
|
|||
int v1, v2;
|
||||
int i;
|
||||
|
||||
if (!face->contents[0].is_valid(options.target_version->game, false))
|
||||
if (!face->contents[0].is_valid(options.target_game, false))
|
||||
Error("Face with invalid contents (%s)", __func__);
|
||||
|
||||
v1 = GetVertex(entity, p1);
|
||||
|
|
|
|||
|
|
@ -357,7 +357,7 @@ FixFaceEdges
|
|||
static void
|
||||
FixFaceEdges(face_t *face, face_t *superface, face_t **facelist)
|
||||
{
|
||||
int i, j, k;
|
||||
int i, j;
|
||||
wedge_t *edge;
|
||||
wvert_t *v;
|
||||
vec_t t1, t2;
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ WAD_LoadInfo(wad_t &wad, bool external)
|
|||
int w = LittleLong(miptex.width);
|
||||
int h = LittleLong(miptex.height);
|
||||
lump.size = sizeof(miptex) + (w>>0)*(h>>0) + (w>>1)*(h>>1) + (w>>2)*(h>>2) + (w>>3)*(h>>3);
|
||||
if (options.target_version == &bspver_hl)
|
||||
if (options.target_game->id == GAME_HALF_LIFE)
|
||||
lump.size += 2+3*256; //palette size+palette data
|
||||
lump.size = (lump.size+3) & ~3; //keep things aligned if we can.
|
||||
|
||||
|
|
@ -219,7 +219,7 @@ WAD_LoadLump(const wad_t &wad, const char *name, uint8_t *dest)
|
|||
memcpy(dest+out->offsets[2], data.data()+(in->offsets[2]), (in->width>>2)*(in->height>>2));
|
||||
memcpy(dest+out->offsets[3], data.data()+(in->offsets[3]), (in->width>>3)*(in->height>>3));
|
||||
|
||||
if (options.target_version == &bspver_hl)
|
||||
if (options.target_game->id == GAME_HALF_LIFE)
|
||||
{ //palette size. 256 in little endian.
|
||||
dest[palofs+0] = ((256>>0)&0xff);
|
||||
dest[palofs+1] = ((256>>8)&0xff);
|
||||
|
|
@ -311,7 +311,7 @@ WADList_Process()
|
|||
}
|
||||
|
||||
// Q2 doesn't use texdata
|
||||
if (options.target_version->game->id == GAME_QUAKE_II) {
|
||||
if (options.target_game->id == GAME_QUAKE_II) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -28,23 +28,10 @@
|
|||
#include <cstdint>
|
||||
|
||||
static void
|
||||
AssertVanillaContentType(int content)
|
||||
AssertVanillaContentType(const contentflags_t &flags)
|
||||
{
|
||||
// TODO
|
||||
if (options.target_version->game->id == GAME_QUAKE_II) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (content) {
|
||||
case CONTENTS_EMPTY:
|
||||
case CONTENTS_SOLID:
|
||||
case CONTENTS_WATER:
|
||||
case CONTENTS_SLIME:
|
||||
case CONTENTS_LAVA:
|
||||
case CONTENTS_SKY:
|
||||
break;
|
||||
default:
|
||||
Error("Internal error: Tried to save compiler-internal contents type %s\n", GetContentsName({ content }));
|
||||
if (!flags.is_valid(options.target_game, false)) {
|
||||
Error("Internal error: Tried to save invalid contents type %s\n", GetContentsName(flags));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -58,7 +45,7 @@ RemapContentsForExport(const contentflags_t &content)
|
|||
*
|
||||
* Normally solid leafs are not written and just referenced as leaf 0.
|
||||
*/
|
||||
return options.target_version->game->create_solid_contents();
|
||||
return options.target_game->create_solid_contents();
|
||||
}
|
||||
|
||||
return content;
|
||||
|
|
@ -195,8 +182,9 @@ ExportLeaf(mapentity_t *entity, node_t *node)
|
|||
map.exported_leafs.push_back({});
|
||||
mleaf_t *dleaf = &map.exported_leafs.back();
|
||||
|
||||
dleaf->contents = RemapContentsForExport(node->contents).native;
|
||||
AssertVanillaContentType(dleaf->contents);
|
||||
const contentflags_t remapped = RemapContentsForExport(node->contents);
|
||||
AssertVanillaContentType(remapped);
|
||||
dleaf->contents = remapped.native;
|
||||
|
||||
/*
|
||||
* write bounding box info
|
||||
|
|
@ -263,7 +251,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.is_solid(options.target_version->game))
|
||||
if (options.target_game->id != GAME_QUAKE_II && node->children[i]->contents.is_solid(options.target_game))
|
||||
dnode->children[i] = -1;
|
||||
else {
|
||||
int nextLeafIndex = static_cast<int>(map.exported_leafs.size());
|
||||
|
|
@ -340,7 +328,7 @@ BeginBSPFile(void)
|
|||
|
||||
// Leave room for leaf 0 (must be solid)
|
||||
map.exported_leafs.push_back({});
|
||||
map.exported_leafs.back().contents = options.target_version->game->create_solid_contents().native;
|
||||
map.exported_leafs.back().contents = options.target_game->create_solid_contents().native;
|
||||
Q_assert(map.exported_leafs.size() == 1);
|
||||
}
|
||||
|
||||
|
|
@ -386,10 +374,10 @@ WriteExtendedTexinfoFlags(void)
|
|||
|
||||
int count = 0;
|
||||
for (const auto &tx : texinfos_sorted) {
|
||||
if (tx.outputnum.has_value())
|
||||
if (!tx.outputnum.has_value())
|
||||
continue;
|
||||
|
||||
Q_assert(count == tx.outputnum); // check we are outputting them in the proper sequence
|
||||
Q_assert(count == tx.outputnum.value()); // check we are outputting them in the proper sequence
|
||||
|
||||
fwrite(&tx.flags, 1, sizeof(tx.flags), texinfofile);
|
||||
count++;
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ set(VIS_SOURCES
|
|||
${VIS_INCLUDES})
|
||||
|
||||
add_executable(vis ${VIS_SOURCES})
|
||||
target_link_libraries (vis ${CMAKE_THREAD_LIBS_INIT})
|
||||
target_link_libraries (vis ${CMAKE_THREAD_LIBS_INIT} fmt::fmt)
|
||||
find_library(M_LIB m)
|
||||
if (M_LIB)
|
||||
target_link_libraries (vis ${M_LIB})
|
||||
|
|
|
|||
Loading…
Reference in New Issue