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})
|
${COMMON_INCLUDES})
|
||||||
|
|
||||||
add_executable(bspinfo ${BSPINFO_SOURCES})
|
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)
|
install(TARGETS bspinfo RUNTIME DESTINATION bin)
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,39 @@
|
||||||
#include <common/cmdlib.hh>
|
#include <common/cmdlib.hh>
|
||||||
#include <common/bspfile.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
|
int
|
||||||
main(int argc, char **argv)
|
main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
|
|
@ -43,6 +76,8 @@ main(int argc, char **argv)
|
||||||
LoadBSPFile(source, &bsp);
|
LoadBSPFile(source, &bsp);
|
||||||
PrintBSPFileSizes(&bsp);
|
PrintBSPFileSizes(&bsp);
|
||||||
|
|
||||||
|
PrintBSPInfo(bsp);
|
||||||
|
|
||||||
printf("---------------------\n");
|
printf("---------------------\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,8 @@
|
||||||
#include <common/bspfile.hh>
|
#include <common/bspfile.hh>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
|
#include <fmt/format.h>
|
||||||
|
|
||||||
struct gamedef_generic_t : public gamedef_t {
|
struct gamedef_generic_t : public gamedef_t {
|
||||||
gamedef_generic_t()
|
gamedef_generic_t()
|
||||||
{
|
{
|
||||||
|
|
@ -72,6 +74,14 @@ struct gamedef_generic_t : public gamedef_t {
|
||||||
bool contents_are_valid(const contentflags_t &, bool) const {
|
bool contents_are_valid(const contentflags_t &, bool) const {
|
||||||
throw std::bad_cast();
|
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>
|
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 {
|
bool contents_are_valid(const contentflags_t &contents, bool strict) const {
|
||||||
return contents.native <= 0;
|
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> {
|
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 {
|
bool contents_are_valid(const contentflags_t &contents, bool strict) const {
|
||||||
if (!strict) {
|
// check that we don't have more than one visible contents type
|
||||||
return true;
|
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);
|
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 *
|
static const char *
|
||||||
BSPVersionString(const bspversion_t *version)
|
BSPVersionString(const bspversion_t *version)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -344,6 +344,8 @@ struct contentflags_t {
|
||||||
bool types_equal(const contentflags_t &other, const gamedef_t *game) const;
|
bool types_equal(const contentflags_t &other, const gamedef_t *game) const;
|
||||||
|
|
||||||
int32_t priority(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 {
|
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_liquid(const contentflags_t &contents) const = 0;
|
||||||
virtual bool contents_are_valid(const contentflags_t &contents, bool strict = true) 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
|
// BSP version struct & instances
|
||||||
|
|
|
||||||
|
|
@ -214,10 +214,10 @@ struct quark_tx_info_t {
|
||||||
std::optional<extended_texinfo_t> info;
|
std::optional<extended_texinfo_t> info;
|
||||||
};
|
};
|
||||||
|
|
||||||
int FindMiptex(const char *name, std::optional<extended_texinfo_t> &extended_info);
|
int FindMiptex(const char *name, std::optional<extended_texinfo_t> &extended_info, bool internal = false);
|
||||||
inline int FindMiptex(const char *name) {
|
inline int FindMiptex(const char *name, bool internal = false) {
|
||||||
std::optional<extended_texinfo_t> extended_info;
|
std::optional<extended_texinfo_t> extended_info;
|
||||||
return FindMiptex(name, extended_info);
|
return FindMiptex(name, extended_info, internal);
|
||||||
}
|
}
|
||||||
int FindTexinfo(const mtexinfo_t &texinfo);
|
int FindTexinfo(const mtexinfo_t &texinfo);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -170,7 +170,7 @@ typedef struct visfacet_s {
|
||||||
struct visfacet_s *original; // face on node
|
struct visfacet_s *original; // face on node
|
||||||
int outputnumber; // only valid for original faces after
|
int outputnumber; // only valid for original faces after
|
||||||
// write surfaces
|
// write surfaces
|
||||||
bool touchesOccupiedLeaf; // internal use in outside.cc
|
bool touchesOccupiedLeaf; // internal use in outside.cc
|
||||||
vec3_t origin;
|
vec3_t origin;
|
||||||
vec_t radius;
|
vec_t radius;
|
||||||
|
|
||||||
|
|
@ -272,6 +272,7 @@ public:
|
||||||
bool fbspx_brushes;
|
bool fbspx_brushes;
|
||||||
bool fNoTextures;
|
bool fNoTextures;
|
||||||
const bspversion_t *target_version = &bspver_q1;
|
const bspversion_t *target_version = &bspver_q1;
|
||||||
|
const gamedef_t *target_game = target_version->game;
|
||||||
int dxSubdivide;
|
int dxSubdivide;
|
||||||
int dxLeakDist;
|
int dxLeakDist;
|
||||||
int maxNodeSize;
|
int maxNodeSize;
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,7 @@ if (embree_FOUND)
|
||||||
endif(embree_FOUND)
|
endif(embree_FOUND)
|
||||||
|
|
||||||
add_executable(light ${LIGHT_SOURCES} main.cc)
|
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)
|
if (embree_FOUND)
|
||||||
target_link_libraries (light PRIVATE embree)
|
target_link_libraries (light PRIVATE embree)
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
add_definitions(-DDOUBLEVEC_T)
|
add_definitions(-DDOUBLEVEC_T)
|
||||||
|
|
||||||
add_executable(qbsp ${QBSP_SOURCES} main.cc)
|
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)
|
install(TARGETS qbsp RUNTIME DESTINATION bin)
|
||||||
|
|
||||||
# test (copied from light/CMakeLists.txt)
|
# test (copied from light/CMakeLists.txt)
|
||||||
|
|
@ -14,4 +14,4 @@ set(QBSP_TEST_SOURCE
|
||||||
add_executable(testqbsp EXCLUDE_FROM_ALL ${QBSP_TEST_SOURCE})
|
add_executable(testqbsp EXCLUDE_FROM_ALL ${QBSP_TEST_SOURCE})
|
||||||
add_test(testqbsp testqbsp)
|
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
|
static int
|
||||||
NormalizePlane(qbsp_plane_t *p)
|
NormalizePlane(qbsp_plane_t *p, bool flip = true)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
vec_t ax, ay, az;
|
vec_t ax, ay, az;
|
||||||
|
|
||||||
p->outputplanenum = PLANENUM_LEAF;
|
|
||||||
|
|
||||||
for (i = 0; i < 3; i++) {
|
for (i = 0; i < 3; i++) {
|
||||||
if (p->normal[i] == 1.0) {
|
if (p->normal[i] == 1.0) {
|
||||||
p->normal[(i + 1) % 3] = 0;
|
p->normal[(i + 1) % 3] = 0;
|
||||||
|
|
@ -184,10 +182,12 @@ NormalizePlane(qbsp_plane_t *p)
|
||||||
return 0; /* no flip */
|
return 0; /* no flip */
|
||||||
}
|
}
|
||||||
if (p->normal[i] == -1.0) {
|
if (p->normal[i] == -1.0) {
|
||||||
|
if (flip) {
|
||||||
p->normal[i] = 1.0;
|
p->normal[i] = 1.0;
|
||||||
|
p->dist = -p->dist;
|
||||||
|
}
|
||||||
p->normal[(i + 1) % 3] = 0;
|
p->normal[(i + 1) % 3] = 0;
|
||||||
p->normal[(i + 2) % 3] = 0;
|
p->normal[(i + 2) % 3] = 0;
|
||||||
p->dist = -p->dist;
|
|
||||||
p->type = PLANE_X + i;
|
p->type = PLANE_X + i;
|
||||||
return 1; /* plane flipped */
|
return 1; /* plane flipped */
|
||||||
}
|
}
|
||||||
|
|
@ -204,7 +204,7 @@ NormalizePlane(qbsp_plane_t *p)
|
||||||
else
|
else
|
||||||
p->type = PLANE_ANYZ;
|
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);
|
VectorSubtract(vec3_origin, p->normal, p->normal);
|
||||||
p->dist = -p->dist;
|
p->dist = -p->dist;
|
||||||
return 1; /* plane flipped */
|
return 1; /* plane flipped */
|
||||||
|
|
@ -262,7 +262,13 @@ NewPlane(const vec3_t normal, const vec_t dist, int *side)
|
||||||
qbsp_plane_t plane;
|
qbsp_plane_t plane;
|
||||||
VectorCopy(normal, plane.normal);
|
VectorCopy(normal, plane.normal);
|
||||||
plane.dist = dist;
|
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();
|
int index = map.planes.size();
|
||||||
map.planes.push_back(plane);
|
map.planes.push_back(plane);
|
||||||
|
|
@ -273,6 +279,7 @@ NewPlane(const vec3_t normal, const vec_t dist, int *side)
|
||||||
/*
|
/*
|
||||||
* FindPlane
|
* FindPlane
|
||||||
* - Returns a global plane number and the side that will be the front
|
* - 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
|
int
|
||||||
FindPlane(const vec3_t normal, const vec_t dist, int *side)
|
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)]) {
|
for (int i : map.planehash[plane_hash_fn(&plane)]) {
|
||||||
const qbsp_plane_t &p = map.planes.at(i);
|
const qbsp_plane_t &p = map.planes.at(i);
|
||||||
if (PlaneEqual(&p, &plane)) {
|
if (PlaneEqual(&p, &plane)) {
|
||||||
|
if (side) {
|
||||||
*side = SIDE_FRONT;
|
*side = SIDE_FRONT;
|
||||||
|
}
|
||||||
return i;
|
return i;
|
||||||
} else if (PlaneInvEqual(&p, &plane)) {
|
} else if (side && PlaneInvEqual(&p, &plane)) {
|
||||||
*side = SIDE_BACK;
|
*side = SIDE_BACK;
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
@ -394,11 +403,11 @@ CreateBrushFaces(const mapentity_t *src, hullbrush_t *hullbrush,
|
||||||
hullbrush->maxs[i] = -VECT_MAX;
|
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;
|
mapface = hullbrush->faces;
|
||||||
for (i = 0; i < hullbrush->numfaces; i++, mapface++) {
|
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 */
|
/* Don't generate hintskip faces */
|
||||||
const mtexinfo_t &texinfo = map.mtexinfos.at(mapface->texinfo);
|
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)
|
(rotate_offset[0] != 0.0 || rotate_offset[1] != 0.0 || rotate_offset[2] != 0.0)
|
||||||
&& rottype == rotation_t::hipnotic
|
&& rottype == rotation_t::hipnotic
|
||||||
&& (hullnum >= 0) // hullnum < 0 corresponds to -wrbrushes clipping hulls
|
&& (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) {
|
if (shouldExpand) {
|
||||||
vec_t delta;
|
vec_t delta;
|
||||||
|
|
@ -849,20 +858,20 @@ Brush_IsDetail(const mapbrush_t *mapbrush)
|
||||||
// contents.
|
// contents.
|
||||||
static bool AdjustContentsFromName(const char *texname, contentflags_t &flags) {
|
static bool AdjustContentsFromName(const char *texname, contentflags_t &flags) {
|
||||||
if (!Q_strcasecmp(texname, "origin"))
|
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"))
|
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"))
|
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] == '*') {
|
else if (texname[0] == '*') {
|
||||||
if (!Q_strncasecmp(texname + 1, "lava", 4))
|
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))
|
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_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_WATER));
|
||||||
}
|
}
|
||||||
else if (!Q_strncasecmp(texname, "sky", 3))
|
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
|
else
|
||||||
return false;
|
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.
|
//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
|
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 mapface_t &mapface = mapbrush->face(i);
|
||||||
const mtexinfo_t &texinfo = map.mtexinfos.at(mapface.texinfo);
|
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))) {
|
if (!is_trans && (texinfo.flags.native & (Q2_SURF_TRANS33 | Q2_SURF_TRANS66))) {
|
||||||
is_trans = true;
|
is_trans = true;
|
||||||
}
|
}
|
||||||
|
|
@ -912,7 +925,8 @@ Brush_GetContents_Q2 (const mapbrush_t *mapbrush, const contentflags_t &base_con
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mapface.contents != contents.native) {
|
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;
|
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
|
// if any side is translucent, mark the contents
|
||||||
// and change solid to window
|
// and change solid to window
|
||||||
if (is_trans) {
|
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
|
// 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;
|
contents.extended |= CFLAGS_ORIGIN;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (contents.native & Q2_CONTENTS_MIST) {
|
||||||
|
contents.extended |= CFLAGS_DETAIL_ILLUSIONARY;
|
||||||
|
}
|
||||||
|
|
||||||
if (is_hint) {
|
if (is_hint) {
|
||||||
contents.extended |= CFLAGS_HINT;
|
contents.extended |= CFLAGS_HINT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: this is a bit of a hack, but this is because clip
|
||||||
|
// and liquids and stuff are already handled *like* detail by
|
||||||
|
// the compiler.
|
||||||
|
if (contents.extended & CFLAGS_DETAIL) {
|
||||||
|
if (!(contents.native & Q2_CONTENTS_SOLID)) {
|
||||||
|
contents.extended &= ~CFLAGS_DETAIL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_assert(contents.is_valid(options.target_game, false));
|
||||||
|
|
||||||
return contents;
|
return contents;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -985,7 +1017,7 @@ brush_t *LoadBrush(const mapentity_t *src, const mapbrush_t *mapbrush, const con
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.target_version == &bspver_hl)
|
if (options.target_game->id == GAME_HALF_LIFE)
|
||||||
{
|
{
|
||||||
if (hullnum == 1) {
|
if (hullnum == 1) {
|
||||||
vec3_t size[2] = { {-16, -16, -36}, {16, 16, 36} };
|
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);
|
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) {
|
if (hullnum == 1) {
|
||||||
vec3_t size[2] = { {-16, -16, -32}, {16, 16, 24} };
|
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 =
|
const bool func_illusionary_visblocker =
|
||||||
(0 == Q_strcasecmp(classname, "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) {
|
if (func_illusionary_visblocker) {
|
||||||
base_contents.extended |= CFLAGS_ILLUSIONARY_VISBLOCKER;
|
base_contents.extended |= CFLAGS_ILLUSIONARY_VISBLOCKER;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: move to game
|
// 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++) {
|
for (int i = 0; i < src->nummapbrushes; i++) {
|
||||||
const mapbrush_t *mapbrush = &src->mapbrush(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;
|
continue;
|
||||||
|
|
||||||
/* turn solid brushes into detail, if we're in hull0 */
|
/* 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) {
|
if (detail) {
|
||||||
contents.extended |= CFLAGS_DETAIL;
|
contents.extended |= CFLAGS_DETAIL;
|
||||||
} else if (detail_illusionary) {
|
} 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) {
|
} 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
|
/* func_detail_illusionary don't exist in the collision hull
|
||||||
* (or bspx export) */
|
* (or bspx export) */
|
||||||
if (hullnum && detail_illusionary) {
|
if ((options.target_game->id != GAME_QUAKE_II && hullnum) && detail_illusionary) {
|
||||||
continue;
|
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 */
|
/* "hint" brushes don't affect the collision hulls */
|
||||||
if (contents.is_hint()) {
|
if (contents.is_hint()) {
|
||||||
if (hullnum)
|
if (hullnum > 0)
|
||||||
continue;
|
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 */
|
/* entities never use water merging */
|
||||||
if (dst != pWorldEnt())
|
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.
|
/* 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.
|
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
|
before writing the bsp, and bmodels normally have CONTENTS_SOLID as their
|
||||||
contents type.
|
contents type.
|
||||||
*/
|
*/
|
||||||
if (dst != pWorldEnt() && hullnum == 0 && (contents.extended & CFLAGS_BMODEL_MIRROR_INSIDE)) {
|
if (dst != pWorldEnt() && hullnum <= 0 && (contents.extended & CFLAGS_BMODEL_MIRROR_INSIDE)) {
|
||||||
contents = contents.merge(options.target_version->game->create_empty_contents(CFLAGS_DETAIL_FENCE));
|
contents = contents.merge(options.target_game->create_empty_contents(CFLAGS_DETAIL_FENCE));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* nonsolid brushes don't show up in clipping hulls */
|
/* nonsolid brushes don't show up in clipping hulls */
|
||||||
// TODO: will this statement need to be modified since clip
|
// TODO: will this statement need to be modified since clip
|
||||||
// detail etc aren't native types any more?
|
// 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;
|
continue;
|
||||||
|
|
||||||
/* sky brushes are solid in the collision hulls */
|
/* sky brushes are solid in the collision hulls */
|
||||||
if (hullnum > 0 && contents.is_sky(options.target_version->game))
|
if (hullnum > 0 && contents.is_sky(options.target_game))
|
||||||
contents = contents.merge(options.target_version->game->create_solid_contents());
|
contents = contents.merge(options.target_game->create_solid_contents());
|
||||||
|
|
||||||
brush_t *brush = LoadBrush(src, mapbrush, contents, rotate_offset, rottype, hullnum);
|
brush_t *brush = LoadBrush(src, mapbrush, contents, rotate_offset, rottype, hullnum);
|
||||||
if (!brush)
|
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)) {
|
} else if (brush->contents.is_detail(CFLAGS_DETAIL_FENCE)) {
|
||||||
brush->next = dst->detail_fence;
|
brush->next = dst->detail_fence;
|
||||||
dst->detail_fence = brush;
|
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;
|
brush->next = dst->solid;
|
||||||
dst->solid = brush;
|
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;
|
brush->next = dst->sky;
|
||||||
dst->sky = brush;
|
dst->sky = brush;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
35
qbsp/csg4.cc
35
qbsp/csg4.cc
|
|
@ -55,9 +55,8 @@ MakeSkipTexinfo()
|
||||||
|
|
||||||
mtexinfo_t mt { };
|
mtexinfo_t mt { };
|
||||||
|
|
||||||
mt.miptex = FindMiptex("skip");
|
mt.miptex = FindMiptex("skip", true);
|
||||||
mt.flags = { 0, TEX_EXFLAG_SKIP };
|
mt.flags = { 0, TEX_EXFLAG_SKIP };
|
||||||
memset(&mt.vecs, 0, sizeof(mt.vecs));
|
|
||||||
|
|
||||||
return FindTexinfo(mt);
|
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
|
// 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.
|
// 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))
|
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 CFLAGS_BMODEL_MIRROR_INSIDE is set, never change to skip
|
||||||
if (!(face->contents[1].extended & CFLAGS_BMODEL_MIRROR_INSIDE)) {
|
if (!(face->contents[1].extended & CFLAGS_BMODEL_MIRROR_INSIDE)) {
|
||||||
|
|
@ -423,7 +422,7 @@ contents override the face inside contents.
|
||||||
static void
|
static void
|
||||||
SaveInsideFaces(face_t *face, const brush_t *clipbrush, face_t **savelist)
|
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;
|
face_t *next;
|
||||||
|
|
||||||
|
|
@ -434,7 +433,7 @@ SaveInsideFaces(face_t *face, const brush_t *clipbrush, face_t **savelist)
|
||||||
next = face->next;
|
next = face->next;
|
||||||
face->contents[0] = clipbrush->contents;
|
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)) {
|
&& clipbrush->contents.is_detail(CFLAGS_DETAIL)) {
|
||||||
// This case is when a structural and detail brush are touching,
|
// This case is when a structural and detail brush are touching,
|
||||||
// and we want to save the sturctural face that is
|
// 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 empty here, and the detail faces have their "back"
|
||||||
// marked as detail.
|
// 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();
|
face->texinfo = MakeSkipTexinfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
// N.B.: We don't need a hack like above for when clipbrush->contents == CONTENTS_DETAIL_ILLUSIONARY.
|
// N.B.: We don't need a hack like above for when clipbrush->contents == CONTENTS_DETAIL_ILLUSIONARY.
|
||||||
|
|
||||||
// These would create leaks
|
// 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)));
|
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)) {
|
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 };
|
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;
|
face->contents[1] = clipbrush->contents;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -533,7 +532,7 @@ CopyBrushFaces(const brush_t *brush)
|
||||||
brushfaces++;
|
brushfaces++;
|
||||||
newface = (face_t *)AllocMem(OTHER, sizeof(face_t), true);
|
newface = (face_t *)AllocMem(OTHER, sizeof(face_t), true);
|
||||||
*newface = *face;
|
*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->contents[1] = brush->contents;
|
||||||
newface->lmshift[0] = brush->lmshift;
|
newface->lmshift[0] = brush->lmshift;
|
||||||
newface->lmshift[1] = 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.
|
// TODO: this might break because this == won't catch the extended types now.
|
||||||
// might need a specific function for this one.
|
// 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()) {
|
&& !clipbrush->contents.clips_same_type()) {
|
||||||
/* _noclipfaces key */
|
/* _noclipfaces key */
|
||||||
continue;
|
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'"
|
* 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)) &&
|
|| ((brush->contents.is_solid(options.target_game) && brush->contents.is_detail(CFLAGS_DETAIL)) &&
|
||||||
(!clipbrush->contents.is_solid(options.target_version->game)
|
(!clipbrush->contents.is_solid(options.target_game)
|
||||||
&& !clipbrush->contents.is_sky(options.target_version->game)
|
&& !clipbrush->contents.is_sky(options.target_game)
|
||||||
&& !clipbrush->contents.is_detail(CFLAGS_DETAIL)))
|
&& !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);
|
SaveInsideFaces(inside, clipbrush, &outside);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -701,7 +700,7 @@ CSGFaces(const mapentity_t *entity)
|
||||||
* All of the faces left on the outside list are real surface faces
|
* 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
|
* 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);
|
SaveFacesToPlaneList(outside, mirror, planefaces);
|
||||||
}
|
}
|
||||||
surface_t *surfaces = BuildSurfaces(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
|
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;
|
const char *pathsep;
|
||||||
int i;
|
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) */
|
/* Ignore leading path in texture names (Q2 map compatibility) */
|
||||||
pathsep = strrchr(name, '/');
|
pathsep = strrchr(name, '/');
|
||||||
if (pathsep)
|
if (pathsep)
|
||||||
name = pathsep + 1;
|
name = pathsep + 1;
|
||||||
|
|
||||||
extended_info = extended_texinfo_t { };
|
|
||||||
|
|
||||||
for (i = 0; i < map.nummiptex(); i++) {
|
for (i = 0; i < map.nummiptex(); i++) {
|
||||||
const texdata_t &tex = map.miptex.at(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 {
|
} else {
|
||||||
// load .wal first
|
// 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
|
if (!internal || !extended_info.has_value()) {
|
||||||
// need to load the wal to figure out if it will match anything
|
wal = LoadWal(name);
|
||||||
// in the list...
|
|
||||||
if (!wal.has_value()) {
|
if (!wal.has_value()) {
|
||||||
Message(msgLiteral, "Couldn't locate wal for %s\n", name);
|
//FIXME
|
||||||
wal = wal_t {};
|
//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++) {
|
for (i = 0; i < map.nummiptex(); i++) {
|
||||||
const texdata_t &tex = map.miptex.at(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();
|
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 */
|
/* Handle animating textures carefully */
|
||||||
if (wal->anim_name[0]) {
|
if (wal && wal->anim_name[0]) {
|
||||||
FindMiptex(wal->anim_name);
|
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 char *texname = map.miptex.at(texinfo.miptex).name.c_str();
|
||||||
const int shadow = atoi(ValueForKey(entity, "_shadow"));
|
const int shadow = atoi(ValueForKey(entity, "_shadow"));
|
||||||
// These flags are pulled from surf flags in Q2.
|
// 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))
|
if (IsSkipName(texname))
|
||||||
flags.extended |= TEX_EXFLAG_SKIP;
|
flags.extended |= TEX_EXFLAG_SKIP;
|
||||||
if (IsHintName(texname))
|
if (IsHintName(texname))
|
||||||
|
|
@ -375,9 +383,15 @@ SurfFlagsForEntity(const mtexinfo_t &texinfo, const mapentity_t *entity)
|
||||||
} else {
|
} else {
|
||||||
flags.native = texinfo.flags.native;
|
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;
|
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;
|
flags.extended |= TEX_EXFLAG_HINT;
|
||||||
}
|
}
|
||||||
if (IsNoExpandName(texname))
|
if (IsNoExpandName(texname))
|
||||||
|
|
@ -488,7 +502,7 @@ ParseEpair(parser_t *parser, mapentity_t *entity)
|
||||||
// Quake II uses multiple starts for level transitions/backtracking.
|
// Quake II uses multiple starts for level transitions/backtracking.
|
||||||
// TODO: instead, this should check targetnames. There should only be
|
// TODO: instead, this should check targetnames. There should only be
|
||||||
// one info_player_start per targetname in Q2.
|
// 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);
|
Message(msgWarning, warnMultipleStarts);
|
||||||
rgfStartSpots |= info_player_start;
|
rgfStartSpots |= info_player_start;
|
||||||
} else if (!Q_strcasecmp(epair->value, "info_player_deathmatch")) {
|
} 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->flags = mapface.flags = { extinfo.info->flags };
|
||||||
tx->value = mapface.value = extinfo.info->value;
|
tx->value = mapface.value = extinfo.info->value;
|
||||||
|
|
||||||
|
Q_assert(contentflags_t { mapface.contents }.is_valid(options.target_game, false));
|
||||||
|
|
||||||
if (!planepts || !plane)
|
if (!planepts || !plane)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
@ -1678,11 +1694,12 @@ ParseBrush(parser_t *parser, const mapentity_t *entity)
|
||||||
if (face.get() == nullptr)
|
if (face.get() == nullptr)
|
||||||
continue;
|
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
|
// translucent objects are automatically classified as detail
|
||||||
//if ((face->flags.native & (Q2_SURF_TRANS33 | Q2_SURF_TRANS66))
|
if ((face->flags.native & (Q2_SURF_TRANS33 | Q2_SURF_TRANS66))
|
||||||
// || (face->contents & (Q2_CONTENTS_PLAYERCLIP | Q2_CONTENTS_MONSTERCLIP)))
|
|| (face->contents & (Q2_CONTENTS_PLAYERCLIP | Q2_CONTENTS_MONSTERCLIP)))
|
||||||
// face->contents |= Q2_CONTENTS_DETAIL;
|
face->contents |= Q2_CONTENTS_DETAIL;
|
||||||
|
|
||||||
if (!(face->contents & (((Q2_LAST_VISIBLE_CONTENTS << 1)-1)
|
if (!(face->contents & (((Q2_LAST_VISIBLE_CONTENTS << 1)-1)
|
||||||
| Q2_CONTENTS_PLAYERCLIP | Q2_CONTENTS_MONSTERCLIP) ) )
|
| Q2_CONTENTS_PLAYERCLIP | Q2_CONTENTS_MONSTERCLIP) ) )
|
||||||
|
|
|
||||||
|
|
@ -338,7 +338,7 @@ ClearOutFaces(node_t *node)
|
||||||
}
|
}
|
||||||
|
|
||||||
// visit the leaf
|
// visit the leaf
|
||||||
if (!node->contents.is_solid(options.target_version->game)) {
|
if (!node->contents.is_solid(options.target_game)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -365,8 +365,8 @@ OutLeafsToSolid_r(node_t *node, int *outleafs_count)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Don't fill sky, or count solids as outleafs
|
// Don't fill sky, or count solids as outleafs
|
||||||
if (node->contents.is_sky(options.target_version->game)
|
if (node->contents.is_sky(options.target_game)
|
||||||
|| node->contents.is_solid(options.target_version->game))
|
|| node->contents.is_solid(options.target_game))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Now check all faces touching the leaf. If any of them are partially going into the occupied part of the map,
|
// 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.
|
// 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;
|
*outleafs_count += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -59,47 +59,42 @@ ClusterContents(const node_t *node)
|
||||||
if (node->planenum == PLANENUM_LEAF)
|
if (node->planenum == PLANENUM_LEAF)
|
||||||
return node->contents;
|
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
|
static bool
|
||||||
PortalThru(const portal_t *p)
|
PortalThru(const portal_t *p)
|
||||||
{
|
{
|
||||||
contentflags_t contents0 = ClusterContents(p->nodes[0]);
|
contentflags_t contents0 = ClusterContents(p->nodes[0]);
|
||||||
contentflags_t contents1 = ClusterContents(p->nodes[1]);
|
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 */
|
/* Can't see through func_illusionary_visblocker */
|
||||||
if ((contents0.extended | contents1.extended) & CFLAGS_ILLUSIONARY_VISBLOCKER)
|
if ((contents0.extended | contents1.extended) & CFLAGS_ILLUSIONARY_VISBLOCKER)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
/* If contents values are the same and not solid, can see through */
|
// FIXME: we can't move this directly to portal_can_see_through because
|
||||||
if (contents0 == contents1)
|
// "options" isn't exposed there.
|
||||||
return true;
|
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 sky is transparent, then sky is like empty space */
|
||||||
if (options.fTranswater) {
|
if (options.fTranssky) {
|
||||||
if (contents0.is_liquid(options.target_version->game) && contents1.is_empty(options.target_version->game))
|
if (contents0.is_sky(options.target_game) && contents1.is_empty(options.target_game))
|
||||||
return true;
|
return true;
|
||||||
if (contents1.is_liquid(options.target_version->game) && contents0.is_empty(options.target_version->game))
|
if (contents0.is_empty(options.target_game) && contents1.is_sky(options.target_game))
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If sky is transparent, then sky is like empty space */
|
// Check per-game visibility
|
||||||
if (options.fTranssky) {
|
return options.target_game->portal_can_see_through(contents0, contents1);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
|
@ -116,7 +111,7 @@ WritePortals_r(node_t *node, FILE *portalFile, bool clusters)
|
||||||
WritePortals_r(node->children[1], portalFile, clusters);
|
WritePortals_r(node->children[1], portalFile, clusters);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (node->contents.is_solid(options.target_version->game))
|
if (node->contents.is_solid(options.target_game))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for (p = node->portals; p; p = next) {
|
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);
|
viscluster = WriteClusters_r(node->children[1], portalFile, viscluster);
|
||||||
return viscluster;
|
return viscluster;
|
||||||
}
|
}
|
||||||
if (node->contents.is_solid(options.target_version->game))
|
if (node->contents.is_solid(options.target_game))
|
||||||
return viscluster;
|
return viscluster;
|
||||||
|
|
||||||
/* If we're in the next cluster, start a new line */
|
/* 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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (node->contents.is_solid(options.target_version->game)) {
|
if (node->contents.is_structural_solid(options.target_game)) {
|
||||||
/* solid block, viewpoint never inside */
|
/* solid block, viewpoint never inside */
|
||||||
node->visleafnum = -1;
|
node->visleafnum = -1;
|
||||||
node->viscluster = -1;
|
node->viscluster = -1;
|
||||||
|
|
@ -380,7 +375,7 @@ MakeHeadnodePortals(const mapentity_t *entity, node_t *node)
|
||||||
}
|
}
|
||||||
|
|
||||||
outside_node.planenum = PLANENUM_LEAF;
|
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;
|
outside_node.portals = NULL;
|
||||||
|
|
||||||
for (i = 0; i < 3; i++)
|
for (i = 0; i < 3; i++)
|
||||||
|
|
@ -671,7 +666,7 @@ PortalizeWorld(const mapentity_t *entity, node_t *headnode, const int hullnum)
|
||||||
MakeHeadnodePortals(entity, headnode);
|
MakeHeadnodePortals(entity, headnode);
|
||||||
CutNodePortals_r(headnode, &state);
|
CutNodePortals_r(headnode, &state);
|
||||||
|
|
||||||
if (!hullnum) {
|
if (hullnum <= 0) {
|
||||||
/* save portal file for vis tracing */
|
/* save portal file for vis tracing */
|
||||||
WritePortalfile(headnode, &state);
|
WritePortalfile(headnode, &state);
|
||||||
|
|
||||||
|
|
|
||||||
114
qbsp/qbsp.cc
114
qbsp/qbsp.cc
|
|
@ -20,7 +20,8 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string.h>
|
#include <cstring>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
#include <common/log.hh>
|
#include <common/log.hh>
|
||||||
#include <common/aabb.hh>
|
#include <common/aabb.hh>
|
||||||
|
|
@ -36,27 +37,38 @@ static const char *IntroString =
|
||||||
options_t options;
|
options_t options;
|
||||||
|
|
||||||
bool node_t::opaque() const {
|
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)
|
static void ExportBrushList_r(const mapentity_t *entity, node_t *node)
|
||||||
{
|
{
|
||||||
if (node->planenum == PLANENUM_LEAF)
|
if (node->planenum == PLANENUM_LEAF)
|
||||||
{
|
{
|
||||||
if (node->visleafnum == -1) {
|
if (node->contents.native) {
|
||||||
node->firstleafbrush = map.exported_leafbrushes.size();
|
|
||||||
node->numleafbrushes = 0;
|
|
||||||
|
|
||||||
int32_t b_id = 0;
|
int32_t b_id = 0;
|
||||||
|
std::vector<uint32_t> brushes;
|
||||||
|
|
||||||
for (const brush_t *b = entity->brushes; b; b = b->next, b_id++)
|
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(
|
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) {
|
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);
|
brushes.push_back(b_id);
|
||||||
node->numleafbrushes++;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
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)
|
static void ExportBrushList(const mapentity_t *entity, node_t *node)
|
||||||
{
|
{
|
||||||
|
brush_state = { };
|
||||||
|
|
||||||
for (const brush_t *b = entity->brushes; b; b = b->next)
|
for (const brush_t *b = entity->brushes; b; b = b->next)
|
||||||
{
|
{
|
||||||
dbrush_t brush { (int32_t) map.exported_brushsides.size(), 0, b->contents.native };
|
dbrush_t brush { (int32_t) map.exported_brushsides.size(), 0, b->contents.native };
|
||||||
|
|
||||||
for (const face_t *f = b->faces; f; f = f->next)
|
for (const face_t *f = b->faces; f; f = f->next)
|
||||||
{
|
{
|
||||||
if (map.planes[f->planenum].outputplanenum == -1) {
|
int32_t planenum = f->planenum;
|
||||||
continue;
|
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.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);
|
map.exported_brushes.push_back(brush);
|
||||||
|
brush_state.total_brushes++;
|
||||||
}
|
}
|
||||||
|
|
||||||
ExportBrushList_r(entity, node);
|
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)
|
if (options.fVerbose)
|
||||||
PrintEntity(entity);
|
PrintEntity(entity);
|
||||||
|
|
||||||
if (hullnum == 0)
|
if (hullnum <= 0)
|
||||||
Message(msgStat, "MODEL: %s", mod);
|
Message(msgStat, "MODEL: %s", mod);
|
||||||
SetKeyValue(entity, "model", mod);
|
SetKeyValue(entity, "model", mod);
|
||||||
}
|
}
|
||||||
|
|
@ -218,11 +282,11 @@ ProcessEntity(mapentity_t *entity, const int hullnum)
|
||||||
*/
|
*/
|
||||||
surfs = CSGFaces(entity);
|
surfs = CSGFaces(entity);
|
||||||
|
|
||||||
if (options.fObjExport && entity == pWorldEnt() && hullnum == 0) {
|
if (options.fObjExport && entity == pWorldEnt() && hullnum <= 0) {
|
||||||
ExportObj_Surfaces("post_csg", surfs);
|
ExportObj_Surfaces("post_csg", surfs);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hullnum != 0) {
|
if (hullnum > 0) {
|
||||||
nodes = SolidBSP(entity, surfs, true);
|
nodes = SolidBSP(entity, surfs, true);
|
||||||
if (entity == pWorldEnt() && !options.fNofill) {
|
if (entity == pWorldEnt() && !options.fNofill) {
|
||||||
// assume non-world bmodels are simple
|
// assume non-world bmodels are simple
|
||||||
|
|
@ -297,11 +361,13 @@ ProcessEntity(mapentity_t *entity, const int hullnum)
|
||||||
}
|
}
|
||||||
|
|
||||||
firstface = MakeFaceEdges(entity, nodes);
|
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);
|
ExportBrushList(entity, nodes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ExportDrawNodes(entity, nodes, firstface);
|
||||||
}
|
}
|
||||||
|
|
||||||
FreeBrushes(entity);
|
FreeBrushes(entity);
|
||||||
|
|
@ -624,6 +690,12 @@ CreateHulls(void)
|
||||||
if (!options.fNoverbose)
|
if (!options.fNoverbose)
|
||||||
options.fVerbose = true;
|
options.fVerbose = true;
|
||||||
|
|
||||||
|
if (options.target_game->id == GAME_QUAKE_II)
|
||||||
|
{
|
||||||
|
CreateSingleHull(-1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
CreateSingleHull(0);
|
CreateSingleHull(0);
|
||||||
|
|
||||||
/* ignore the clipping hulls altogether */
|
/* ignore the clipping hulls altogether */
|
||||||
|
|
@ -633,9 +705,10 @@ CreateHulls(void)
|
||||||
CreateSingleHull(1);
|
CreateSingleHull(1);
|
||||||
CreateSingleHull(2);
|
CreateSingleHull(2);
|
||||||
|
|
||||||
if (options.target_version == &bspver_hl)
|
// FIXME: use game->get_hull_count
|
||||||
|
if (options.target_game->id == GAME_HALF_LIFE)
|
||||||
CreateSingleHull(3);
|
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*/
|
{ /*note: h2mp doesn't use hull 2 automatically, however gamecode can explicitly set ent.hull=3 to access it*/
|
||||||
CreateSingleHull(3);
|
CreateSingleHull(3);
|
||||||
CreateSingleHull(4);
|
CreateSingleHull(4);
|
||||||
|
|
@ -1033,9 +1106,12 @@ ParseOptions(char *szOptions)
|
||||||
}
|
}
|
||||||
|
|
||||||
// force specific flags for Q2
|
// 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;
|
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.
|
// If both children are solid, we can merge the two leafs into one.
|
||||||
// DarkPlaces has an assertion that fails if both children are
|
// DarkPlaces has an assertion that fails if both children are
|
||||||
// solid.
|
// solid.
|
||||||
if (node->children[0]->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_version->game)) {
|
&& node->children[1]->contents.is_structural_solid(options.target_game)) {
|
||||||
// This discards any faces on-node. Should be safe (?)
|
// 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;
|
surf->has_struct = false;
|
||||||
|
|
||||||
for (const face_t *f = surf->faces; f; f = f->next) {
|
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))
|
for (auto &contents : f->contents)
|
||||||
Error("Bad contents in face (%s)", __func__);
|
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];
|
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) {
|
for (face_t *f = surf->faces; f; f = f->next) {
|
||||||
count++;
|
count++;
|
||||||
|
|
||||||
const int currentpri = contents.has_value() ? contents->priority(options.target_version->game) : -1;
|
const int currentpri = contents.has_value() ? contents->priority(options.target_game) : -1;
|
||||||
const int fpri = f->contents[0].priority(options.target_version->game);
|
const int fpri = f->contents[0].priority(options.target_game);
|
||||||
if (fpri > currentpri) {
|
if (fpri > currentpri) {
|
||||||
contents = f->contents[0];
|
contents = f->contents[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
// HACK: Handle structural covered by detail.
|
// HACK: Handle structural covered by detail.
|
||||||
if (f->contents[0].extended & CFLAGS_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;
|
contents = solid_detail;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -773,7 +774,7 @@ LinkConvexFaces(surface_t *planelist, node_t *leafnode)
|
||||||
// NOTE: This is crazy..
|
// NOTE: This is crazy..
|
||||||
// Liquid leafs get assigned liquid content types because of the
|
// Liquid leafs get assigned liquid content types because of the
|
||||||
// "cosmetic" mirrored faces.
|
// "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) {
|
if (leafnode->contents.extended & CFLAGS_ILLUSIONARY_VISBLOCKER) {
|
||||||
c_illusionary_visblocker++;
|
c_illusionary_visblocker++;
|
||||||
|
|
@ -783,14 +784,14 @@ LinkConvexFaces(surface_t *planelist, node_t *leafnode)
|
||||||
c_detail_illusionary++;
|
c_detail_illusionary++;
|
||||||
} else if (leafnode->contents.extended & CFLAGS_DETAIL) {
|
} else if (leafnode->contents.extended & CFLAGS_DETAIL) {
|
||||||
c_detail++;
|
c_detail++;
|
||||||
} else if (leafnode->contents.is_empty(options.target_version->game)) {
|
} else if (leafnode->contents.is_empty(options.target_game)) {
|
||||||
c_empty++;
|
c_empty++;
|
||||||
} else if (leafnode->contents.is_solid(options.target_version->game)) {
|
} else if (leafnode->contents.is_solid(options.target_game)) {
|
||||||
c_solid++;
|
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++;
|
c_water++;
|
||||||
} else {
|
} 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
|
// 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] = (node_t *)AllocMem(OTHER, sizeof(node_t), true);
|
||||||
headnode->children[0]->planenum = PLANENUM_LEAF;
|
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[0]->markfaces = (face_t **)AllocMem(OTHER, sizeof(face_t *), true);
|
||||||
headnode->children[1] = (node_t *)AllocMem(OTHER, sizeof(node_t), true);
|
headnode->children[1] = (node_t *)AllocMem(OTHER, sizeof(node_t), true);
|
||||||
headnode->children[1]->planenum = PLANENUM_LEAF;
|
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);
|
headnode->children[1]->markfaces = (face_t **)AllocMem(OTHER, sizeof(face_t *), true);
|
||||||
|
|
||||||
return headnode;
|
return headnode;
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ SubdivideFace(face_t *f, face_t **prevptr)
|
||||||
{
|
{
|
||||||
vec_t mins, maxs;
|
vec_t mins, maxs;
|
||||||
vec_t v;
|
vec_t v;
|
||||||
int axis, i;
|
int axis;
|
||||||
qbsp_plane_t plane;
|
qbsp_plane_t plane;
|
||||||
face_t *front, *back, *next;
|
face_t *front, *back, *next;
|
||||||
const mtexinfo_t *tex;
|
const mtexinfo_t *tex;
|
||||||
|
|
@ -49,7 +49,7 @@ SubdivideFace(face_t *f, face_t **prevptr)
|
||||||
tex = &map.mtexinfos.at(f->texinfo);
|
tex = &map.mtexinfos.at(f->texinfo);
|
||||||
|
|
||||||
if (tex->flags.extended & (TEX_EXFLAG_SKIP | TEX_EXFLAG_HINT) ||
|
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;
|
return;
|
||||||
//subdivision is pretty much pointless other than because of lightmap block limits
|
//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
|
//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 v1, v2;
|
||||||
int i;
|
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__);
|
Error("Face with invalid contents (%s)", __func__);
|
||||||
|
|
||||||
v1 = GetVertex(entity, p1);
|
v1 = GetVertex(entity, p1);
|
||||||
|
|
|
||||||
|
|
@ -357,7 +357,7 @@ FixFaceEdges
|
||||||
static void
|
static void
|
||||||
FixFaceEdges(face_t *face, face_t *superface, face_t **facelist)
|
FixFaceEdges(face_t *face, face_t *superface, face_t **facelist)
|
||||||
{
|
{
|
||||||
int i, j, k;
|
int i, j;
|
||||||
wedge_t *edge;
|
wedge_t *edge;
|
||||||
wvert_t *v;
|
wvert_t *v;
|
||||||
vec_t t1, t2;
|
vec_t t1, t2;
|
||||||
|
|
|
||||||
|
|
@ -77,7 +77,7 @@ WAD_LoadInfo(wad_t &wad, bool external)
|
||||||
int w = LittleLong(miptex.width);
|
int w = LittleLong(miptex.width);
|
||||||
int h = LittleLong(miptex.height);
|
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);
|
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 += 2+3*256; //palette size+palette data
|
||||||
lump.size = (lump.size+3) & ~3; //keep things aligned if we can.
|
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[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));
|
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.
|
{ //palette size. 256 in little endian.
|
||||||
dest[palofs+0] = ((256>>0)&0xff);
|
dest[palofs+0] = ((256>>0)&0xff);
|
||||||
dest[palofs+1] = ((256>>8)&0xff);
|
dest[palofs+1] = ((256>>8)&0xff);
|
||||||
|
|
@ -311,7 +311,7 @@ WADList_Process()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Q2 doesn't use texdata
|
// Q2 doesn't use texdata
|
||||||
if (options.target_version->game->id == GAME_QUAKE_II) {
|
if (options.target_game->id == GAME_QUAKE_II) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,23 +28,10 @@
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
static void
|
static void
|
||||||
AssertVanillaContentType(int content)
|
AssertVanillaContentType(const contentflags_t &flags)
|
||||||
{
|
{
|
||||||
// TODO
|
if (!flags.is_valid(options.target_game, false)) {
|
||||||
if (options.target_version->game->id == GAME_QUAKE_II) {
|
Error("Internal error: Tried to save invalid contents type %s\n", GetContentsName(flags));
|
||||||
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 }));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -58,7 +45,7 @@ RemapContentsForExport(const contentflags_t &content)
|
||||||
*
|
*
|
||||||
* Normally solid leafs are not written and just referenced as leaf 0.
|
* 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;
|
return content;
|
||||||
|
|
@ -195,8 +182,9 @@ ExportLeaf(mapentity_t *entity, node_t *node)
|
||||||
map.exported_leafs.push_back({});
|
map.exported_leafs.push_back({});
|
||||||
mleaf_t *dleaf = &map.exported_leafs.back();
|
mleaf_t *dleaf = &map.exported_leafs.back();
|
||||||
|
|
||||||
dleaf->contents = RemapContentsForExport(node->contents).native;
|
const contentflags_t remapped = RemapContentsForExport(node->contents);
|
||||||
AssertVanillaContentType(dleaf->contents);
|
AssertVanillaContentType(remapped);
|
||||||
|
dleaf->contents = remapped.native;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* write bounding box info
|
* write bounding box info
|
||||||
|
|
@ -263,7 +251,7 @@ ExportDrawNodes(mapentity_t *entity, node_t *node)
|
||||||
if (node->children[i]->planenum == PLANENUM_LEAF) {
|
if (node->children[i]->planenum == PLANENUM_LEAF) {
|
||||||
// In Q2, all leaves must have their own ID even if they share solidity.
|
// In Q2, all leaves must have their own ID even if they share solidity.
|
||||||
// (probably for collision purposes? makes sense given they store leafbrushes)
|
// (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;
|
dnode->children[i] = -1;
|
||||||
else {
|
else {
|
||||||
int nextLeafIndex = static_cast<int>(map.exported_leafs.size());
|
int nextLeafIndex = static_cast<int>(map.exported_leafs.size());
|
||||||
|
|
@ -340,7 +328,7 @@ BeginBSPFile(void)
|
||||||
|
|
||||||
// Leave room for leaf 0 (must be solid)
|
// Leave room for leaf 0 (must be solid)
|
||||||
map.exported_leafs.push_back({});
|
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);
|
Q_assert(map.exported_leafs.size() == 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -386,10 +374,10 @@ WriteExtendedTexinfoFlags(void)
|
||||||
|
|
||||||
int count = 0;
|
int count = 0;
|
||||||
for (const auto &tx : texinfos_sorted) {
|
for (const auto &tx : texinfos_sorted) {
|
||||||
if (tx.outputnum.has_value())
|
if (!tx.outputnum.has_value())
|
||||||
continue;
|
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);
|
fwrite(&tx.flags, 1, sizeof(tx.flags), texinfofile);
|
||||||
count++;
|
count++;
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ set(VIS_SOURCES
|
||||||
${VIS_INCLUDES})
|
${VIS_INCLUDES})
|
||||||
|
|
||||||
add_executable(vis ${VIS_SOURCES})
|
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)
|
find_library(M_LIB m)
|
||||||
if (M_LIB)
|
if (M_LIB)
|
||||||
target_link_libraries (vis ${M_LIB})
|
target_link_libraries (vis ${M_LIB})
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue