From eb4bcf96dc2105de196ed3488f54f3ab2ad08db3 Mon Sep 17 00:00:00 2001 From: Jonathan Date: Sat, 9 Oct 2021 14:14:49 -0400 Subject: [PATCH] -includeskip for Q2RTX Fix missing fmt include Default nexttexinfo to -1 to match other tools Fmt support for bspversion_t, use that instead of a static function Cleanup write functions --- common/bspfile.cc | 792 ++++++++++++++++++-------------------- include/common/bspfile.hh | 44 ++- include/common/qvec.hh | 3 - include/qbsp/qbsp.hh | 1 + man/qbsp.1 | 2 + qbsp/exportobj.cc | 1 + qbsp/qbsp.cc | 9 +- qbsp/surfaces.cc | 14 +- qbsp/writebsp.cc | 3 +- 9 files changed, 433 insertions(+), 436 deletions(-) diff --git a/common/bspfile.cc b/common/bspfile.cc index 4596b371..0f9d4c7e 100644 --- a/common/bspfile.cc +++ b/common/bspfile.cc @@ -25,164 +25,6 @@ #include -/* - * ========================================================================= - * ... - * ========================================================================= - */ - -constexpr lumpspec_t lumpspec_bsp29[] = { - {"entities", sizeof(char)}, - {"planes", sizeof(dplane_t)}, - {"texture", sizeof(uint8_t)}, - {"vertexes", sizeof(qvec3f)}, - {"visibility", sizeof(uint8_t)}, - {"nodes", sizeof(bsp29_dnode_t)}, - {"texinfos", sizeof(texinfo_t)}, - {"faces", sizeof(bsp29_dface_t)}, - {"lighting", sizeof(uint8_t)}, - {"clipnodes", sizeof(bsp29_dclipnode_t)}, - {"leafs", sizeof(bsp29_dleaf_t)}, - {"marksurfaces", sizeof(uint16_t)}, - {"edges", sizeof(bsp29_dedge_t)}, - {"surfedges", sizeof(int32_t)}, - {"models", sizeof(dmodelq1_t)}, -}; - -constexpr lumpspec_t lumpspec_bsp2rmq[] = { - {"entities", sizeof(char)}, - {"planes", sizeof(dplane_t)}, - {"texture", sizeof(uint8_t)}, - {"vertexes", sizeof(qvec3f)}, - {"visibility", sizeof(uint8_t)}, - {"nodes", sizeof(bsp2rmq_dnode_t)}, - {"texinfos", sizeof(texinfo_t)}, - {"faces", sizeof(bsp2_dface_t)}, - {"lighting", sizeof(uint8_t)}, - {"clipnodes", sizeof(bsp2_dclipnode_t)}, - {"leafs", sizeof(bsp2rmq_dleaf_t)}, - {"marksurfaces", sizeof(uint32_t)}, - {"edges", sizeof(bsp2_dedge_t)}, - {"surfedges", sizeof(int32_t)}, - {"models", sizeof(dmodelq1_t)}, -}; - -constexpr lumpspec_t lumpspec_bsp2[] = { - {"entities", sizeof(char)}, - {"planes", sizeof(dplane_t)}, - {"texture", sizeof(uint8_t)}, - {"vertexes", sizeof(qvec3f)}, - {"visibility", sizeof(uint8_t)}, - {"nodes", sizeof(bsp2_dnode_t)}, - {"texinfos", sizeof(texinfo_t)}, - {"faces", sizeof(bsp2_dface_t)}, - {"lighting", sizeof(uint8_t)}, - {"clipnodes", sizeof(bsp2_dclipnode_t)}, - {"leafs", sizeof(bsp2_dleaf_t)}, - {"marksurfaces", sizeof(uint32_t)}, - {"edges", sizeof(bsp2_dedge_t)}, - {"surfedges", sizeof(int32_t)}, - {"models", sizeof(dmodelq1_t)}, -}; - -constexpr lumpspec_t lumpspec_bsp29_h2[] = { - {"entities", sizeof(char)}, - {"planes", sizeof(dplane_t)}, - {"texture", sizeof(uint8_t)}, - {"vertexes", sizeof(qvec3f)}, - {"visibility", sizeof(uint8_t)}, - {"nodes", sizeof(bsp29_dnode_t)}, - {"texinfos", sizeof(texinfo_t)}, - {"faces", sizeof(bsp29_dface_t)}, - {"lighting", sizeof(uint8_t)}, - {"clipnodes", sizeof(bsp29_dclipnode_t)}, - {"leafs", sizeof(bsp29_dleaf_t)}, - {"marksurfaces", sizeof(uint16_t)}, - {"edges", sizeof(bsp29_dedge_t)}, - {"surfedges", sizeof(int32_t)}, - {"models", sizeof(dmodelh2_t)}, -}; - -constexpr lumpspec_t lumpspec_bsp2rmq_h2[] = { - {"entities", sizeof(char)}, - {"planes", sizeof(dplane_t)}, - {"texture", sizeof(uint8_t)}, - {"vertexes", sizeof(qvec3f)}, - {"visibility", sizeof(uint8_t)}, - {"nodes", sizeof(bsp2rmq_dnode_t)}, - {"texinfos", sizeof(texinfo_t)}, - {"faces", sizeof(bsp2_dface_t)}, - {"lighting", sizeof(uint8_t)}, - {"clipnodes", sizeof(bsp2_dclipnode_t)}, - {"leafs", sizeof(bsp2rmq_dleaf_t)}, - {"marksurfaces", sizeof(uint32_t)}, - {"edges", sizeof(bsp2_dedge_t)}, - {"surfedges", sizeof(int32_t)}, - {"models", sizeof(dmodelh2_t)}, -}; - -constexpr lumpspec_t lumpspec_bsp2_h2[] = { - {"entities", sizeof(char)}, - {"planes", sizeof(dplane_t)}, - {"texture", sizeof(uint8_t)}, - {"vertexes", sizeof(qvec3f)}, - {"visibility", sizeof(uint8_t)}, - {"nodes", sizeof(bsp2_dnode_t)}, - {"texinfos", sizeof(texinfo_t)}, - {"faces", sizeof(bsp2_dface_t)}, - {"lighting", sizeof(uint8_t)}, - {"clipnodes", sizeof(bsp2_dclipnode_t)}, - {"leafs", sizeof(bsp2_dleaf_t)}, - {"marksurfaces", sizeof(uint32_t)}, - {"edges", sizeof(bsp2_dedge_t)}, - {"surfedges", sizeof(int32_t)}, - {"models", sizeof(dmodelh2_t)}, -}; - -constexpr lumpspec_t lumpspec_q2bsp[] = { - {"entities", sizeof(char)}, - {"planes", sizeof(dplane_t)}, - {"vertexes", sizeof(qvec3f)}, - {"visibility", sizeof(uint8_t)}, - {"nodes", sizeof(q2_dnode_t)}, - {"texinfos", sizeof(q2_texinfo_t)}, - {"faces", sizeof(q2_dface_t)}, - {"lighting", sizeof(uint8_t)}, - {"leafs", sizeof(q2_dleaf_t)}, - {"leaffaces", sizeof(uint16_t)}, - {"leafbrushes", sizeof(uint16_t)}, - {"edges", sizeof(bsp29_dedge_t)}, - {"surfedges", sizeof(int32_t)}, - {"models", sizeof(q2_dmodel_t)}, - {"brushes", sizeof(dbrush_t)}, - {"brushsides", sizeof(q2_dbrushside_t)}, - {"pop", sizeof(uint8_t)}, - {"areas", sizeof(darea_t)}, - {"areaportals", sizeof(dareaportal_t)}, -}; - -constexpr lumpspec_t lumpspec_qbism[] = { - {"entities", sizeof(char)}, - {"planes", sizeof(dplane_t)}, - {"vertexes", sizeof(qvec3f)}, - {"visibility", sizeof(uint8_t)}, - {"nodes", sizeof(q2_dnode_qbism_t)}, - {"texinfos", sizeof(q2_texinfo_t)}, - {"faces", sizeof(q2_dface_qbism_t)}, - {"lighting", sizeof(uint8_t)}, - {"leafs", sizeof(q2_dleaf_qbism_t)}, - {"leaffaces", sizeof(uint32_t)}, - {"leafbrushes", sizeof(uint32_t)}, - {"edges", sizeof(bsp2_dedge_t)}, - {"surfedges", sizeof(int32_t)}, - {"models", sizeof(q2_dmodel_t)}, - {"brushes", sizeof(dbrush_t)}, - {"brushsides", sizeof(q2_dbrushside_qbism_t)}, - {"pop", sizeof(uint8_t)}, - {"areas", sizeof(darea_t)}, - {"areaportals", sizeof(dareaportal_t)}, -}; - struct gamedef_generic_t : public gamedef_t { gamedef_generic_t() : gamedef_t("") { id = GAME_UNKNOWN; } @@ -576,27 +418,165 @@ struct gamedef_q2_t : public gamedef_t } }; +// Game definitions, used for the bsp versions below static const gamedef_generic_t gamedef_generic; -const bspversion_t bspver_generic{NO_VERSION, NO_VERSION, "mbsp", "generic BSP", nullptr, &gamedef_generic}; static const gamedef_q1_like_t gamedef_q1; -const bspversion_t bspver_q1{BSPVERSION, NO_VERSION, "bsp29", "Quake BSP", lumpspec_bsp29, &gamedef_q1, &bspver_bsp2}; -const bspversion_t bspver_bsp2{BSP2VERSION, NO_VERSION, "bsp2", "Quake BSP2", lumpspec_bsp2, &gamedef_q1}; -const bspversion_t bspver_bsp2rmq{ - BSP2RMQVERSION, NO_VERSION, "bsp2rmq", "Quake BSP2-RMQ", lumpspec_bsp2rmq, &gamedef_q1}; -/* Hexen II doesn't use a separate version, but we can still use a separate tag/name for it */ static const gamedef_h2_t gamedef_h2; -const bspversion_t bspver_h2{ - BSPVERSION, NO_VERSION, "hexen2", "Hexen II BSP", lumpspec_bsp29_h2, &gamedef_h2, &bspver_h2bsp2}; -const bspversion_t bspver_h2bsp2{BSP2VERSION, NO_VERSION, "hexen2bsp2", "Hexen II BSP2", lumpspec_bsp2_h2, &gamedef_h2}; -const bspversion_t bspver_h2bsp2rmq{ - BSP2RMQVERSION, NO_VERSION, "hexen2bsp2rmq", "Hexen II BSP2-RMQ", lumpspec_bsp2rmq_h2, &gamedef_h2}; static const gamedef_hl_t gamedef_hl; -const bspversion_t bspver_hl{BSPHLVERSION, NO_VERSION, "hl", "Half-Life BSP", lumpspec_bsp29, &gamedef_hl}; static const gamedef_q2_t gamedef_q2; + +const bspversion_t bspver_generic{NO_VERSION, NO_VERSION, "mbsp", "generic BSP", {}, &gamedef_generic}; +const bspversion_t bspver_q1{BSPVERSION, NO_VERSION, "bsp29", "Quake BSP", { + {"entities", sizeof(char)}, + {"planes", sizeof(dplane_t)}, + {"texture", sizeof(uint8_t)}, + {"vertexes", sizeof(qvec3f)}, + {"visibility", sizeof(uint8_t)}, + {"nodes", sizeof(bsp29_dnode_t)}, + {"texinfos", sizeof(texinfo_t)}, + {"faces", sizeof(bsp29_dface_t)}, + {"lighting", sizeof(uint8_t)}, + {"clipnodes", sizeof(bsp29_dclipnode_t)}, + {"leafs", sizeof(bsp29_dleaf_t)}, + {"marksurfaces", sizeof(uint16_t)}, + {"edges", sizeof(bsp29_dedge_t)}, + {"surfedges", sizeof(int32_t)}, + {"models", sizeof(dmodelq1_t)}, +}, &gamedef_q1, &bspver_bsp2}; +const bspversion_t bspver_bsp2{BSP2VERSION, NO_VERSION, "bsp2", "Quake BSP2", { + {"entities", sizeof(char)}, + {"planes", sizeof(dplane_t)}, + {"texture", sizeof(uint8_t)}, + {"vertexes", sizeof(qvec3f)}, + {"visibility", sizeof(uint8_t)}, + {"nodes", sizeof(bsp2_dnode_t)}, + {"texinfos", sizeof(texinfo_t)}, + {"faces", sizeof(bsp2_dface_t)}, + {"lighting", sizeof(uint8_t)}, + {"clipnodes", sizeof(bsp2_dclipnode_t)}, + {"leafs", sizeof(bsp2_dleaf_t)}, + {"marksurfaces", sizeof(uint32_t)}, + {"edges", sizeof(bsp2_dedge_t)}, + {"surfedges", sizeof(int32_t)}, + {"models", sizeof(dmodelq1_t)}, +}, &gamedef_q1}; +const bspversion_t bspver_bsp2rmq{ + BSP2RMQVERSION, NO_VERSION, "bsp2rmq", "Quake BSP2-RMQ", { + {"entities", sizeof(char)}, + {"planes", sizeof(dplane_t)}, + {"texture", sizeof(uint8_t)}, + {"vertexes", sizeof(qvec3f)}, + {"visibility", sizeof(uint8_t)}, + {"nodes", sizeof(bsp2rmq_dnode_t)}, + {"texinfos", sizeof(texinfo_t)}, + {"faces", sizeof(bsp2_dface_t)}, + {"lighting", sizeof(uint8_t)}, + {"clipnodes", sizeof(bsp2_dclipnode_t)}, + {"leafs", sizeof(bsp2rmq_dleaf_t)}, + {"marksurfaces", sizeof(uint32_t)}, + {"edges", sizeof(bsp2_dedge_t)}, + {"surfedges", sizeof(int32_t)}, + {"models", sizeof(dmodelq1_t)}, +}, &gamedef_q1}; +/* Hexen II doesn't use a separate version, but we can still use a separate tag/name for it */ +const bspversion_t bspver_h2{ + BSPVERSION, NO_VERSION, "hexen2", "Hexen II BSP", { + {"entities", sizeof(char)}, + {"planes", sizeof(dplane_t)}, + {"texture", sizeof(uint8_t)}, + {"vertexes", sizeof(qvec3f)}, + {"visibility", sizeof(uint8_t)}, + {"nodes", sizeof(bsp29_dnode_t)}, + {"texinfos", sizeof(texinfo_t)}, + {"faces", sizeof(bsp29_dface_t)}, + {"lighting", sizeof(uint8_t)}, + {"clipnodes", sizeof(bsp29_dclipnode_t)}, + {"leafs", sizeof(bsp29_dleaf_t)}, + {"marksurfaces", sizeof(uint16_t)}, + {"edges", sizeof(bsp29_dedge_t)}, + {"surfedges", sizeof(int32_t)}, + {"models", sizeof(dmodelh2_t)}, +}, &gamedef_h2, &bspver_h2bsp2}; +const bspversion_t bspver_h2bsp2{BSP2VERSION, NO_VERSION, "hexen2bsp2", "Hexen II BSP2", { + {"entities", sizeof(char)}, + {"planes", sizeof(dplane_t)}, + {"texture", sizeof(uint8_t)}, + {"vertexes", sizeof(qvec3f)}, + {"visibility", sizeof(uint8_t)}, + {"nodes", sizeof(bsp2_dnode_t)}, + {"texinfos", sizeof(texinfo_t)}, + {"faces", sizeof(bsp2_dface_t)}, + {"lighting", sizeof(uint8_t)}, + {"clipnodes", sizeof(bsp2_dclipnode_t)}, + {"leafs", sizeof(bsp2_dleaf_t)}, + {"marksurfaces", sizeof(uint32_t)}, + {"edges", sizeof(bsp2_dedge_t)}, + {"surfedges", sizeof(int32_t)}, + {"models", sizeof(dmodelh2_t)}, +}, &gamedef_h2}; +const bspversion_t bspver_h2bsp2rmq{ + BSP2RMQVERSION, NO_VERSION, "hexen2bsp2rmq", "Hexen II BSP2-RMQ", { + {"entities", sizeof(char)}, + {"planes", sizeof(dplane_t)}, + {"texture", sizeof(uint8_t)}, + {"vertexes", sizeof(qvec3f)}, + {"visibility", sizeof(uint8_t)}, + {"nodes", sizeof(bsp2rmq_dnode_t)}, + {"texinfos", sizeof(texinfo_t)}, + {"faces", sizeof(bsp2_dface_t)}, + {"lighting", sizeof(uint8_t)}, + {"clipnodes", sizeof(bsp2_dclipnode_t)}, + {"leafs", sizeof(bsp2rmq_dleaf_t)}, + {"marksurfaces", sizeof(uint32_t)}, + {"edges", sizeof(bsp2_dedge_t)}, + {"surfedges", sizeof(int32_t)}, + {"models", sizeof(dmodelh2_t)}, +}, &gamedef_h2}; +const bspversion_t bspver_hl{BSPHLVERSION, NO_VERSION, "hl", "Half-Life BSP", bspver_q1.lumps, &gamedef_hl}; const bspversion_t bspver_q2{ - Q2_BSPIDENT, Q2_BSPVERSION, "q2bsp", "Quake II BSP", lumpspec_q2bsp, &gamedef_q2, &bspver_qbism}; + Q2_BSPIDENT, Q2_BSPVERSION, "q2bsp", "Quake II BSP", { + {"entities", sizeof(char)}, + {"planes", sizeof(dplane_t)}, + {"vertexes", sizeof(qvec3f)}, + {"visibility", sizeof(uint8_t)}, + {"nodes", sizeof(q2_dnode_t)}, + {"texinfos", sizeof(q2_texinfo_t)}, + {"faces", sizeof(q2_dface_t)}, + {"lighting", sizeof(uint8_t)}, + {"leafs", sizeof(q2_dleaf_t)}, + {"leaffaces", sizeof(uint16_t)}, + {"leafbrushes", sizeof(uint16_t)}, + {"edges", sizeof(bsp29_dedge_t)}, + {"surfedges", sizeof(int32_t)}, + {"models", sizeof(q2_dmodel_t)}, + {"brushes", sizeof(dbrush_t)}, + {"brushsides", sizeof(q2_dbrushside_t)}, + {"pop", sizeof(uint8_t)}, + {"areas", sizeof(darea_t)}, + {"areaportals", sizeof(dareaportal_t)}, +}, &gamedef_q2, &bspver_qbism}; const bspversion_t bspver_qbism{ - Q2_QBISMIDENT, Q2_BSPVERSION, "qbism", "Quake II Qbism BSP", lumpspec_qbism, &gamedef_q2}; + Q2_QBISMIDENT, Q2_BSPVERSION, "qbism", "Quake II Qbism BSP", { + {"entities", sizeof(char)}, + {"planes", sizeof(dplane_t)}, + {"vertexes", sizeof(qvec3f)}, + {"visibility", sizeof(uint8_t)}, + {"nodes", sizeof(q2_dnode_qbism_t)}, + {"texinfos", sizeof(q2_texinfo_t)}, + {"faces", sizeof(q2_dface_qbism_t)}, + {"lighting", sizeof(uint8_t)}, + {"leafs", sizeof(q2_dleaf_qbism_t)}, + {"leaffaces", sizeof(uint32_t)}, + {"leafbrushes", sizeof(uint32_t)}, + {"edges", sizeof(bsp2_dedge_t)}, + {"surfedges", sizeof(int32_t)}, + {"models", sizeof(q2_dmodel_t)}, + {"brushes", sizeof(dbrush_t)}, + {"brushsides", sizeof(q2_dbrushside_qbism_t)}, + {"pop", sizeof(uint8_t)}, + {"areas", sizeof(darea_t)}, + {"areaportals", sizeof(dareaportal_t)}, +}, &gamedef_q2}; bool contentflags_t::types_equal(const contentflags_t &other, const gamedef_t *game) const { @@ -640,23 +620,6 @@ std::string contentflags_t::to_string(const gamedef_t *game) const return s; } -static const char *BSPVersionString(const bspversion_t *version) -{ - if (version->name) { - return version->name; - } - - static char buffers[2][20]; - static int index; - char *buffer = buffers[1 & ++index]; - if (version->version != NO_VERSION) { - snprintf(buffer, sizeof(buffers[0]), "%d:%d", version->version, version->ident); - } else { - snprintf(buffer, sizeof(buffers[0]), "%d", version->version); - } - return buffer; -} - static bool BSPVersionSupported(int32_t ident, int32_t version, const bspversion_t **out_version) { for (const bspversion_t *bspver : bspversions) { @@ -928,7 +891,8 @@ struct lump_reader template void read(size_t lump_num, std::vector &buffer) { - const lumpspec_t &lumpspec = version->lumps[lump_num]; + Q_assert(version->lumps.size() > lump_num); + const lumpspec_t &lumpspec = version->lumps.begin()[lump_num]; const lump_t &lump = lumps[lump_num]; size_t length; @@ -963,7 +927,8 @@ struct lump_reader // read string from stream void read(size_t lump_num, std::string &buffer) { - const lumpspec_t &lumpspec = version->lumps[lump_num]; + Q_assert(version->lumps.size() > lump_num); + const lumpspec_t &lumpspec = version->lumps.begin()[lump_num]; const lump_t &lump = lumps[lump_num]; Q_assert(lumpspec.size == 1); @@ -988,7 +953,8 @@ struct lump_reader template>> void read(size_t lump_num, T &buffer) { - const lumpspec_t &lumpspec = version->lumps[lump_num]; + Q_assert(version->lumps.size() > lump_num); + const lumpspec_t &lumpspec = version->lumps.begin()[lump_num]; const lump_t &lump = lumps[lump_num]; if (!lump.filelen) @@ -1098,7 +1064,7 @@ void LoadBSPFile(std::filesystem::path &filename, bspdata_t *bspdata) /* check the file version */ if (!BSPVersionSupported(temp_version.ident, temp_version.version, &bspdata->version)) { - LogPrint("BSP is version {}\n", BSPVersionString(&temp_version)); + LogPrint("BSP is version {}\n", temp_version); Error("Sorry, this bsp version is not supported."); } else { // special case handling for Hexen II @@ -1112,7 +1078,7 @@ void LoadBSPFile(std::filesystem::path &filename, bspdata_t *bspdata) } } - LogPrint("BSP is version {}\n", BSPVersionString(bspdata->version)); + LogPrint("BSP is version {}\n", *bspdata->version); } lump_reader reader{stream, bspdata->version, lumps}; @@ -1183,192 +1149,195 @@ struct bspfile_t }; std::ofstream stream; -}; -// write structured lump data from vector -template -static void WriteLump(bspfile_t &bspfile, size_t lump_num, const std::vector &data) -{ - static constexpr char pad[4]{}; - const lumpspec_t &lumpspec = bspfile.version->lumps[lump_num]; - lump_t *lumps; - - if (bspfile.version->version != NO_VERSION) { - lumps = bspfile.q2header.lumps.data(); - } else { - lumps = bspfile.q1header.lumps.data(); - } - - lump_t &lump = lumps[lump_num]; - - lump.fileofs = bspfile.stream.tellp(); - - for (auto &v : data) - bspfile.stream <= v; - - auto written = static_cast(bspfile.stream.tellp()) - lump.fileofs; - - if (sizeof(T) == 1 || lumpspec.size > 1) - Q_assert(written == (lumpspec.size * data.size())); - - lump.filelen = written; - - if (written % 4) - bspfile.stream.write(pad, 4 - (written % 4)); -} - -// write structured string data -static void WriteLump(bspfile_t &bspfile, size_t lump_num, const std::string &data) -{ - static constexpr char pad[4]{}; - const lumpspec_t &lumpspec = bspfile.version->lumps[lump_num]; - lump_t *lumps; - - Q_assert(lumpspec.size == 1); - - if (bspfile.version->version != NO_VERSION) { - lumps = bspfile.q2header.lumps.data(); - } else { - lumps = bspfile.q1header.lumps.data(); - } - - lump_t &lump = lumps[lump_num]; - - lump.fileofs = bspfile.stream.tellp(); - - bspfile.stream.write(data.c_str(), data.size() + 1); // null terminator - - auto written = static_cast(bspfile.stream.tellp()) - lump.fileofs; - - Q_assert(written == data.size() + 1); - - lump.filelen = written; - - if (written % 4) - bspfile.stream.write(pad, 4 - (written % 4)); -} - -// write structured lump data -template>> -static void WriteLump(bspfile_t &bspfile, size_t lump_num, const T &data) -{ - static constexpr char pad[4]{}; - const lumpspec_t &lumpspec = bspfile.version->lumps[lump_num]; - lump_t *lumps; - - Q_assert(lumpspec.size == 1); - - if (bspfile.version->version != NO_VERSION) { - lumps = bspfile.q2header.lumps.data(); - } else { - lumps = bspfile.q1header.lumps.data(); - } - - lump_t &lump = lumps[lump_num]; - - lump.fileofs = bspfile.stream.tellp(); - - data.stream_write(bspfile.stream); - - auto written = static_cast(bspfile.stream.tellp()) - lump.fileofs; - - lump.filelen = written; - - if (written % 4) - bspfile.stream.write(pad, 4 - (written % 4)); -} - -template -inline void WriteQ1BSP(bspfile_t &bspfile, const T &bsp) -{ - WriteLump(bspfile, LUMP_PLANES, bsp.dplanes); - WriteLump(bspfile, LUMP_LEAFS, bsp.dleafs); - WriteLump(bspfile, LUMP_VERTEXES, bsp.dvertexes); - WriteLump(bspfile, LUMP_NODES, bsp.dnodes); - WriteLump(bspfile, LUMP_TEXINFO, bsp.texinfo); - WriteLump(bspfile, LUMP_FACES, bsp.dfaces); - WriteLump(bspfile, LUMP_CLIPNODES, bsp.dclipnodes); - WriteLump(bspfile, LUMP_MARKSURFACES, bsp.dmarksurfaces); - WriteLump(bspfile, LUMP_SURFEDGES, bsp.dsurfedges); - WriteLump(bspfile, LUMP_EDGES, bsp.dedges); - if (std::holds_alternative(bsp.dmodels)) { - WriteLump(bspfile, LUMP_MODELS, std::get(bsp.dmodels)); - } else { - WriteLump(bspfile, LUMP_MODELS, std::get(bsp.dmodels)); - } - WriteLump(bspfile, LUMP_LIGHTING, bsp.dlightdata); - WriteLump(bspfile, LUMP_VISIBILITY, bsp.dvisdata); - WriteLump(bspfile, LUMP_ENTITIES, bsp.dentdata); - if (std::holds_alternative(bsp.dtex)) { - WriteLump(bspfile, LUMP_TEXTURES, std::get(bsp.dtex)); - } else { - WriteLump(bspfile, LUMP_TEXTURES, std::get(bsp.dtex)); - } -} - -template -inline void WriteQ2BSP(bspfile_t &bspfile, const T &bsp) -{ - WriteLump(bspfile, Q2_LUMP_PLANES, bsp.dplanes); - WriteLump(bspfile, Q2_LUMP_LEAFS, bsp.dleafs); - WriteLump(bspfile, Q2_LUMP_VERTEXES, bsp.dvertexes); - WriteLump(bspfile, Q2_LUMP_NODES, bsp.dnodes); - WriteLump(bspfile, Q2_LUMP_TEXINFO, bsp.texinfo); - WriteLump(bspfile, Q2_LUMP_FACES, bsp.dfaces); - WriteLump(bspfile, Q2_LUMP_LEAFFACES, bsp.dleaffaces); - WriteLump(bspfile, Q2_LUMP_SURFEDGES, bsp.dsurfedges); - WriteLump(bspfile, Q2_LUMP_EDGES, bsp.dedges); - WriteLump(bspfile, Q2_LUMP_MODELS, bsp.dmodels); - WriteLump(bspfile, Q2_LUMP_LEAFBRUSHES, bsp.dleafbrushes); - WriteLump(bspfile, Q2_LUMP_BRUSHES, bsp.dbrushes); - WriteLump(bspfile, Q2_LUMP_BRUSHSIDES, bsp.dbrushsides); - WriteLump(bspfile, Q2_LUMP_AREAS, bsp.dareas); - WriteLump(bspfile, Q2_LUMP_AREAPORTALS, bsp.dareaportals); - - WriteLump(bspfile, Q2_LUMP_LIGHTING, bsp.dlightdata); - WriteLump(bspfile, Q2_LUMP_VISIBILITY, bsp.dvis); - WriteLump(bspfile, Q2_LUMP_ENTITIES, bsp.dentdata); -} - -inline void WriteBSPXLumps(bspdata_t *bspdata, bspfile_t &bspfile) -{ - if (!bspdata->bspx.entries.size()) - return; - - if (bspfile.stream.tellp() & 3) - FError("BSPX header is misaligned"); - - bspfile.stream <= bspx_header_t(bspdata->bspx.entries.size()); - - auto bspxheader = bspfile.stream.tellp(); - - // write dummy lump headers - for (auto &x : bspdata->bspx.entries) { - bspfile.stream <= bspx_lump_t{}; - } - - std::vector xlumps; - xlumps.reserve(bspdata->bspx.entries.size()); - - for (auto &x : bspdata->bspx.entries) { +private: + // write structured lump data from vector + template + inline void write_lump(size_t lump_num, const std::vector &data) + { static constexpr char pad[4]{}; + Q_assert(version->lumps.size() > lump_num); + const lumpspec_t &lumpspec = version->lumps.begin()[lump_num]; + lump_t *lumps; - bspx_lump_t &lump = xlumps.emplace_back(); - lump.filelen = x.second.lumpsize; - lump.fileofs = bspfile.stream.tellp(); - memcpy(lump.lumpname.data(), x.first.c_str(), std::min(x.first.size(), lump.lumpname.size() - 1)); + if (version->version != NO_VERSION) { + lumps = q2header.lumps.data(); + } else { + lumps = q1header.lumps.data(); + } - bspfile.stream.write(reinterpret_cast(x.second.lumpdata.get()), x.second.lumpsize); + lump_t &lump = lumps[lump_num]; - if (x.second.lumpsize % 4) - bspfile.stream.write(pad, 4 - (x.second.lumpsize % 4)); + lump.fileofs = stream.tellp(); + + for (auto &v : data) + stream <= v; + + auto written = static_cast(stream.tellp()) - lump.fileofs; + + if (sizeof(T) == 1 || lumpspec.size > 1) + Q_assert(written == (lumpspec.size * data.size())); + + lump.filelen = written; + + if (written % 4) + stream.write(pad, 4 - (written % 4)); } - bspfile.stream.seekp(bspxheader); + // this is only here to satisfy std::visit + constexpr void write_lump(size_t, const std::monostate &) { } - for (auto &lump : xlumps) - bspfile.stream <= lump; -} + // write structured string data + inline void write_lump(size_t lump_num, const std::string &data) + { + Q_assert(version->lumps.size() > lump_num); + static constexpr char pad[4]{}; + const lumpspec_t &lumpspec = version->lumps.begin()[lump_num]; + lump_t *lumps; + + Q_assert(lumpspec.size == 1); + + if (version->version != NO_VERSION) { + lumps = q2header.lumps.data(); + } else { + lumps = q1header.lumps.data(); + } + + lump_t &lump = lumps[lump_num]; + + lump.fileofs = stream.tellp(); + + stream.write(data.c_str(), data.size() + 1); // null terminator + + auto written = static_cast(stream.tellp()) - lump.fileofs; + + Q_assert(written == data.size() + 1); + + lump.filelen = written; + + if (written % 4) + stream.write(pad, 4 - (written % 4)); + } + + // write structured lump data + template>> + inline void write_lump(size_t lump_num, const T &data) + { + static constexpr char pad[4]{}; + const lumpspec_t &lumpspec = version->lumps.begin()[lump_num]; + lump_t *lumps; + + Q_assert(lumpspec.size == 1); + + if (version->version != NO_VERSION) { + lumps = q2header.lumps.data(); + } else { + lumps = q1header.lumps.data(); + } + + lump_t &lump = lumps[lump_num]; + + lump.fileofs = stream.tellp(); + + data.stream_write(stream); + + auto written = static_cast(stream.tellp()) - lump.fileofs; + + lump.filelen = written; + + if (written % 4) + stream.write(pad, 4 - (written % 4)); + } + +public: + inline void write_bsp(const mbsp_t &) { FError("Can't write generic BSP"); } + inline void write_bsp(const std::monostate &) { FError("No BSP to write"); } + + template, int> = 0> + inline void write_bsp(const T &bsp) + { + write_lump(LUMP_PLANES, bsp.dplanes); + write_lump(LUMP_LEAFS, bsp.dleafs); + write_lump(LUMP_VERTEXES, bsp.dvertexes); + write_lump(LUMP_NODES, bsp.dnodes); + write_lump(LUMP_TEXINFO, bsp.texinfo); + write_lump(LUMP_FACES, bsp.dfaces); + write_lump(LUMP_CLIPNODES, bsp.dclipnodes); + write_lump(LUMP_MARKSURFACES, bsp.dmarksurfaces); + write_lump(LUMP_SURFEDGES, bsp.dsurfedges); + write_lump(LUMP_EDGES, bsp.dedges); + std::visit([this](auto&& arg) { write_lump(LUMP_MODELS, arg); }, bsp.dmodels); + + write_lump(LUMP_LIGHTING, bsp.dlightdata); + write_lump(LUMP_VISIBILITY, bsp.dvisdata); + write_lump(LUMP_ENTITIES, bsp.dentdata); + std::visit([this](auto&& arg) { write_lump(LUMP_TEXTURES, arg); }, bsp.dtex); + } + + template, int> = 0> + inline void write_bsp(const T &bsp) + { + write_lump(Q2_LUMP_PLANES, bsp.dplanes); + write_lump(Q2_LUMP_LEAFS, bsp.dleafs); + write_lump(Q2_LUMP_VERTEXES, bsp.dvertexes); + write_lump(Q2_LUMP_NODES, bsp.dnodes); + write_lump(Q2_LUMP_TEXINFO, bsp.texinfo); + write_lump(Q2_LUMP_FACES, bsp.dfaces); + write_lump(Q2_LUMP_LEAFFACES, bsp.dleaffaces); + write_lump(Q2_LUMP_SURFEDGES, bsp.dsurfedges); + write_lump(Q2_LUMP_EDGES, bsp.dedges); + write_lump(Q2_LUMP_MODELS, bsp.dmodels); + write_lump(Q2_LUMP_LEAFBRUSHES, bsp.dleafbrushes); + write_lump(Q2_LUMP_BRUSHES, bsp.dbrushes); + write_lump(Q2_LUMP_BRUSHSIDES, bsp.dbrushsides); + write_lump(Q2_LUMP_AREAS, bsp.dareas); + write_lump(Q2_LUMP_AREAPORTALS, bsp.dareaportals); + + write_lump(Q2_LUMP_LIGHTING, bsp.dlightdata); + write_lump(Q2_LUMP_VISIBILITY, bsp.dvis); + write_lump(Q2_LUMP_ENTITIES, bsp.dentdata); + } + + inline void write_bspx(const bspdata_t &bspdata) + { + if (!bspdata.bspx.entries.size()) + return; + + if (stream.tellp() & 3) + FError("BSPX header is misaligned"); + + stream <= bspx_header_t(bspdata.bspx.entries.size()); + + auto bspxheader = stream.tellp(); + + // write dummy lump headers + for (auto &x : bspdata.bspx.entries) { + stream <= bspx_lump_t{}; + } + + std::vector xlumps; + xlumps.reserve(bspdata.bspx.entries.size()); + + for (auto &x : bspdata.bspx.entries) { + static constexpr char pad[4]{}; + + bspx_lump_t &lump = xlumps.emplace_back(); + lump.filelen = x.second.lumpsize; + lump.fileofs = stream.tellp(); + memcpy(lump.lumpname.data(), x.first.c_str(), std::min(x.first.size(), lump.lumpname.size() - 1)); + + stream.write(reinterpret_cast(x.second.lumpdata.get()), x.second.lumpsize); + + if (x.second.lumpsize % 4) + stream.write(pad, 4 - (x.second.lumpsize % 4)); + } + + stream.seekp(bspxheader); + + for (auto &lump : xlumps) + stream <= lump; + } +}; /* * ============= @@ -1389,7 +1358,7 @@ void WriteBSPFile(const std::filesystem::path &filename, bspdata_t *bspdata) bspfile.q2header.version = bspfile.version->version; } - LogPrint("Writing {} as BSP version {}\n", filename, BSPVersionString(bspdata->version)); + LogPrint("Writing {} as BSP version {}\n", filename, *bspdata->version); bspfile.stream.open(filename, std::ios_base::out | std::ios_base::trunc | std::ios_base::binary); if (!bspfile.stream) @@ -1404,22 +1373,10 @@ void WriteBSPFile(const std::filesystem::path &filename, bspdata_t *bspdata) bspfile.stream <= bspfile.q1header; } - if (std::holds_alternative(bspdata->bsp)) { - WriteQ1BSP(bspfile, std::get(bspdata->bsp)); - } else if (std::holds_alternative(bspdata->bsp)) { - WriteQ1BSP(bspfile, std::get(bspdata->bsp)); - } else if (std::holds_alternative(bspdata->bsp)) { - WriteQ1BSP(bspfile, std::get(bspdata->bsp)); - } else if (std::holds_alternative(bspdata->bsp)) { - WriteQ2BSP(bspfile, std::get(bspdata->bsp)); - } else if (std::holds_alternative(bspdata->bsp)) { - WriteQ2BSP(bspfile, std::get(bspdata->bsp)); - } else { - FError("Unknown format"); - } + std::visit([&bspfile](auto&& arg) { bspfile.write_bsp(arg); }, bspdata->bsp); /*BSPX lumps are at a 4-byte alignment after the last of any official lump*/ - WriteBSPXLumps(bspdata, bspfile); + bspfile.write_bspx(*bspdata); bspfile.stream.seekp(0); @@ -1433,30 +1390,29 @@ void WriteBSPFile(const std::filesystem::path &filename, bspdata_t *bspdata) /* ========================================================================= */ -static void PrintLumpSize(const lumpspec_t *lumpspec, int lumptype, int count) +inline void PrintLumpSize(const lumpspec_t &lump, size_t count) { - const lumpspec_t *lump = &lumpspec[lumptype]; - LogPrint("{:7} {:<12} {:10}\n", count, lump->name, count * lump->size); + LogPrint("{:7} {:<12} {:10}\n", count, lump.name, count * lump.size); } template -inline void PrintQ1BSPLumps(const lumpspec_t *lumpspec, const T &bsp) +inline void PrintQ1BSPLumps(const std::initializer_list &lumpspec, const T &bsp) { if (std::holds_alternative(bsp.dmodels)) LogPrint("{:7} {:<12}\n", std::get(bsp.dmodels).size(), "models"); else LogPrint("{:7} {:<12}\n", std::get(bsp.dmodels).size(), "models"); - PrintLumpSize(lumpspec, LUMP_PLANES, bsp.dplanes.size()); - PrintLumpSize(lumpspec, LUMP_VERTEXES, bsp.dvertexes.size()); - PrintLumpSize(lumpspec, LUMP_NODES, bsp.dnodes.size()); - PrintLumpSize(lumpspec, LUMP_TEXINFO, bsp.texinfo.size()); - PrintLumpSize(lumpspec, LUMP_FACES, bsp.dfaces.size()); - PrintLumpSize(lumpspec, LUMP_CLIPNODES, bsp.dclipnodes.size()); - PrintLumpSize(lumpspec, LUMP_LEAFS, bsp.dleafs.size()); - PrintLumpSize(lumpspec, LUMP_MARKSURFACES, bsp.dmarksurfaces.size()); - PrintLumpSize(lumpspec, LUMP_EDGES, bsp.dedges.size()); - PrintLumpSize(lumpspec, LUMP_SURFEDGES, bsp.dsurfedges.size()); + PrintLumpSize(lumpspec.begin()[LUMP_PLANES], bsp.dplanes.size()); + PrintLumpSize(lumpspec.begin()[LUMP_VERTEXES], bsp.dvertexes.size()); + PrintLumpSize(lumpspec.begin()[LUMP_NODES], bsp.dnodes.size()); + PrintLumpSize(lumpspec.begin()[LUMP_TEXINFO], bsp.texinfo.size()); + PrintLumpSize(lumpspec.begin()[LUMP_FACES], bsp.dfaces.size()); + PrintLumpSize(lumpspec.begin()[LUMP_CLIPNODES], bsp.dclipnodes.size()); + PrintLumpSize(lumpspec.begin()[LUMP_LEAFS], bsp.dleafs.size()); + PrintLumpSize(lumpspec.begin()[LUMP_MARKSURFACES], bsp.dmarksurfaces.size()); + PrintLumpSize(lumpspec.begin()[LUMP_EDGES], bsp.dedges.size()); + PrintLumpSize(lumpspec.begin()[LUMP_SURFEDGES], bsp.dsurfedges.size()); if (std::holds_alternative(bsp.dtex)) LogPrint("{:7} {:<12} {:10}\n", "", "textures", std::get(bsp.dtex).textures.size()); @@ -1468,24 +1424,24 @@ inline void PrintQ1BSPLumps(const lumpspec_t *lumpspec, const T &bsp) } template -inline void PrintQ2BSPLumps(const lumpspec_t *lumpspec, const T &bsp) +inline void PrintQ2BSPLumps(const std::initializer_list &lumpspec, const T &bsp) { LogPrint("{:7} {:<12}\n", bsp.dmodels.size(), "models"); - PrintLumpSize(lumpspec, Q2_LUMP_PLANES, bsp.dplanes.size()); - PrintLumpSize(lumpspec, Q2_LUMP_VERTEXES, bsp.dvertexes.size()); - PrintLumpSize(lumpspec, Q2_LUMP_NODES, bsp.dnodes.size()); - PrintLumpSize(lumpspec, Q2_LUMP_TEXINFO, bsp.texinfo.size()); - PrintLumpSize(lumpspec, Q2_LUMP_FACES, bsp.dfaces.size()); - PrintLumpSize(lumpspec, Q2_LUMP_LEAFS, bsp.dleafs.size()); - PrintLumpSize(lumpspec, Q2_LUMP_LEAFFACES, bsp.dleaffaces.size()); - PrintLumpSize(lumpspec, Q2_LUMP_LEAFBRUSHES, bsp.dleafbrushes.size()); - PrintLumpSize(lumpspec, Q2_LUMP_EDGES, bsp.dedges.size()); - PrintLumpSize(lumpspec, Q2_LUMP_SURFEDGES, bsp.dsurfedges.size()); - PrintLumpSize(lumpspec, Q2_LUMP_BRUSHES, bsp.dbrushes.size()); - PrintLumpSize(lumpspec, Q2_LUMP_BRUSHSIDES, bsp.dbrushsides.size()); - PrintLumpSize(lumpspec, Q2_LUMP_AREAS, bsp.dareas.size()); - PrintLumpSize(lumpspec, Q2_LUMP_AREAPORTALS, bsp.dareaportals.size()); + PrintLumpSize(lumpspec.begin()[Q2_LUMP_PLANES], bsp.dplanes.size()); + PrintLumpSize(lumpspec.begin()[Q2_LUMP_VERTEXES], bsp.dvertexes.size()); + PrintLumpSize(lumpspec.begin()[Q2_LUMP_NODES], bsp.dnodes.size()); + PrintLumpSize(lumpspec.begin()[Q2_LUMP_TEXINFO], bsp.texinfo.size()); + PrintLumpSize(lumpspec.begin()[Q2_LUMP_FACES], bsp.dfaces.size()); + PrintLumpSize(lumpspec.begin()[Q2_LUMP_LEAFS], bsp.dleafs.size()); + PrintLumpSize(lumpspec.begin()[Q2_LUMP_LEAFFACES], bsp.dleaffaces.size()); + PrintLumpSize(lumpspec.begin()[Q2_LUMP_LEAFBRUSHES], bsp.dleafbrushes.size()); + PrintLumpSize(lumpspec.begin()[Q2_LUMP_EDGES], bsp.dedges.size()); + PrintLumpSize(lumpspec.begin()[Q2_LUMP_SURFEDGES], bsp.dsurfedges.size()); + PrintLumpSize(lumpspec.begin()[Q2_LUMP_BRUSHES], bsp.dbrushes.size()); + PrintLumpSize(lumpspec.begin()[Q2_LUMP_BRUSHSIDES], bsp.dbrushsides.size()); + PrintLumpSize(lumpspec.begin()[Q2_LUMP_AREAS], bsp.dareas.size()); + PrintLumpSize(lumpspec.begin()[Q2_LUMP_AREAPORTALS], bsp.dareaportals.size()); LogPrint("{:7} {:<12} {:10}\n", "", "lightdata", bsp.dlightdata.size()); LogPrint("{:7} {:<12} {:10}\n", "", "visdata", bsp.dvis.bits.size()); @@ -1500,7 +1456,7 @@ inline void PrintQ2BSPLumps(const lumpspec_t *lumpspec, const T &bsp) */ void PrintBSPFileSizes(const bspdata_t *bspdata) { - const lumpspec_t *lumpspec = bspdata->version->lumps; + const auto &lumpspec = bspdata->version->lumps; if (std::holds_alternative(bspdata->bsp)) { PrintQ2BSPLumps(lumpspec, std::get(bspdata->bsp)); @@ -1513,7 +1469,7 @@ void PrintBSPFileSizes(const bspdata_t *bspdata) } else if (std::holds_alternative(bspdata->bsp)) { PrintQ1BSPLumps(lumpspec, std::get(bspdata->bsp)); } else { - Error("Unsupported BSP version: {}", BSPVersionString(bspdata->version)); + Error("Unsupported BSP version: {}", *bspdata->version); } for (auto &x : bspdata->bspx.entries) { diff --git a/include/common/bspfile.hh b/include/common/bspfile.hh index 66a80f41..5f277771 100644 --- a/include/common/bspfile.hh +++ b/include/common/bspfile.hh @@ -829,7 +829,7 @@ struct gtexinfo_t // q2 only int32_t value; // light emission, etc std::array texture; // texture name (textures/*.wal) - int32_t nexttexinfo; // for animations, -1 = end of chain + int32_t nexttexinfo = -1; // for animations, -1 = end of chain }; struct texinfo_t @@ -1398,7 +1398,10 @@ using dmodelh2_vector = std::vector; using miptexq1_lump = dmiptexlump_t; using miptexhl_lump = dmiptexlump_t; -struct bsp29_t +// type tag used for type inference +struct q1bsp_tag_t { }; + +struct bsp29_t : q1bsp_tag_t { std::variant dmodels; std::vector dvisdata; @@ -1417,7 +1420,7 @@ struct bsp29_t std::vector dsurfedges; }; -struct bsp2rmq_t +struct bsp2rmq_t : q1bsp_tag_t { std::variant dmodels; std::vector dvisdata; @@ -1436,7 +1439,7 @@ struct bsp2rmq_t std::vector dsurfedges; }; -struct bsp2_t +struct bsp2_t : q1bsp_tag_t { std::variant dmodels; std::vector dvisdata; @@ -1455,7 +1458,10 @@ struct bsp2_t std::vector dsurfedges; }; -struct q2bsp_t +// type tag used for type inference +struct q2bsp_tag_t { }; + +struct q2bsp_t : q2bsp_tag_t { std::vector dmodels; @@ -1479,7 +1485,7 @@ struct q2bsp_t std::vector dbrushsides; }; -struct q2bsp_qbism_t +struct q2bsp_qbism_t : q2bsp_tag_t { std::vector dmodels; mvis_t dvis; @@ -1657,13 +1663,37 @@ struct bspversion_t /* full display name for printing */ const char *name; /* lump specification */ - const lumpspec_t *lumps; + const std::initializer_list lumps; /* game ptr */ const gamedef_t *game; /* if we surpass the limits of this format, upgrade to this one */ const bspversion_t *extended_limits; }; +// FMT support +template<> +struct fmt::formatter +{ + constexpr auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) + { + return ctx.end(); + } + + template + auto format(const bspversion_t &v, FormatContext &ctx) -> decltype(ctx.out()) + { + if (v.name) { + return format_to(ctx.out(), "{}", v.name); + } + + if (v.version != NO_VERSION) { + return format_to(ctx.out(), "{}:{}", v.version, v.ident); + } + + return format_to(ctx.out(), "{}", v.version, v.ident); + } +}; + extern const bspversion_t bspver_generic; extern const bspversion_t bspver_q1; extern const bspversion_t bspver_h2; diff --git a/include/common/qvec.hh b/include/common/qvec.hh index ee40566f..cde05c69 100644 --- a/include/common/qvec.hh +++ b/include/common/qvec.hh @@ -607,9 +607,6 @@ namespace qv [[nodiscard]] qmat2x2f inverse(const qmat2x2f &input); }; // namespace qv -// FMT support -#include - template struct fmt::formatter> : formatter { diff --git a/include/qbsp/qbsp.hh b/include/qbsp/qbsp.hh index 8339b922..71f19ce0 100644 --- a/include/qbsp/qbsp.hh +++ b/include/qbsp/qbsp.hh @@ -295,6 +295,7 @@ public: bool fContentHack; vec_t worldExtent; bool fNoThreads; + bool includeSkip; options_t() : fNofill(false), fNoclip(false), fNoskip(false), fNodetail(false), fOnlyents(false), fConvertMapFormat(false), diff --git a/man/qbsp.1 b/man/qbsp.1 index c0c7af14..9ac148b3 100644 --- a/man/qbsp.1 +++ b/man/qbsp.1 @@ -96,6 +96,8 @@ Detail brushes are omitted from the compile. Convert a .MAP to a different .MAP format. fmt can be: quake, quake2, valve, bp (brush primitives). Conversions to "quake" or "quake2" format may not be able to match the texture alignment in the source map, other conversions are lossless. The converted map is saved to -.map. +.IP "\fB-includeskip\fP" +Emit skip/nodraw faces. Mainly for Q2RTX. .SH "SPECIAL TEXTURE NAMES" .PP diff --git a/qbsp/exportobj.cc b/qbsp/exportobj.cc index 743c9aa6..926cd134 100644 --- a/qbsp/exportobj.cc +++ b/qbsp/exportobj.cc @@ -171,6 +171,7 @@ static void ExportObj_Marksurfaces_r(const node_t *node, std::unordered_setmarkfaces; *markface; markface++) { face_t *face = *markface; + if (map.mtexinfos.at(face->texinfo).flags.extended & TEX_EXFLAG_SKIP) continue; diff --git a/qbsp/qbsp.cc b/qbsp/qbsp.cc index dcd1bedb..465c8926 100644 --- a/qbsp/qbsp.cc +++ b/qbsp/qbsp.cc @@ -162,7 +162,7 @@ static std::vector> AddBrushBevels(const brush_t *b int32_t planenum = FindPlane(&new_plane.normal[0], new_plane.dist, nullptr); int32_t outputplanenum = ExportMapPlane(planenum); - planes.emplace_back(outputplanenum, nullptr); + planes.emplace_back(outputplanenum, b->faces); } // if the plane is not in it canonical order, swap it @@ -250,7 +250,7 @@ static std::vector> AddBrushBevels(const brush_t *b // add this plane int32_t planenum = FindPlane(¤t.normal[0], current.dist, nullptr); int32_t outputplanenum = ExportMapPlane(planenum); - planes.emplace_back(outputplanenum, nullptr); + planes.emplace_back(outputplanenum, b->faces); } } } @@ -273,7 +273,7 @@ static void ExportBrushList(const mapentity_t *entity, node_t *node, uint32_t &b for (auto &plane : bevels) { map.bsp.dbrushsides.push_back( - {(uint32_t)std::get<0>(plane), (int32_t)ExportMapTexinfo(b->faces->texinfo)}); + {(uint32_t)std::get<0>(plane), (int32_t)ExportMapTexinfo(std::get<1>(plane)->texinfo)}); brush.numsides++; brush_state.total_brush_sides++; } @@ -1153,6 +1153,7 @@ PrintOptions " -omitdetailillusionary func_detail_illusionary brushes are omitted from the compile\n" " -omitdetailfence func_detail_fence brushes are omitted from the compile\n" " -convert Convert a .MAP to a different .MAP format. fmt can be: quake, quake2, valve, bp (brush primitives).\n" + " -includeskip Include skip/nodraw faces." " -expand Write hull 1 expanded brushes to expanded.map for debugging\n" " -leaktest Make compilation fail if the map leaks\n" " -contenthack Hack to fix leaks through solids. Causes missing faces in some cases so disabled by default.\n" @@ -1378,6 +1379,8 @@ static void ParseOptions(char *szOptions) options.fConvertMapFormat = true; szTok = szTok2; + } else if (!Q_strcasecmp(szTok, "includeskip")) { + options.includeSkip = true; } else if (!Q_strcasecmp(szTok, "forceprt1")) { options.fForcePRT1 = true; LogPrint("WARNING: Forcing creation of PRT1.\n"); diff --git a/qbsp/surfaces.cc b/qbsp/surfaces.cc index fc6b08c4..d2b1558a 100644 --- a/qbsp/surfaces.cc +++ b/qbsp/surfaces.cc @@ -303,7 +303,9 @@ FindFaceEdges */ static void FindFaceEdges(mapentity_t *entity, face_t *face) { - if (map.mtexinfos.at(face->texinfo).flags.extended & (TEX_EXFLAG_SKIP | TEX_EXFLAG_HINT)) + if (!options.includeSkip && (map.mtexinfos.at(face->texinfo).flags.extended & TEX_EXFLAG_SKIP)) + return; + if (map.mtexinfos.at(face->texinfo).flags.extended & TEX_EXFLAG_HINT) return; face->outputnumber = std::nullopt; @@ -348,8 +350,10 @@ EmitFace static void EmitFace(mapentity_t *entity, face_t *face) { int i; - - if (map.mtexinfos.at(face->texinfo).flags.extended & (TEX_EXFLAG_SKIP | TEX_EXFLAG_HINT)) + + if (!options.includeSkip && (map.mtexinfos.at(face->texinfo).flags.extended & TEX_EXFLAG_SKIP)) + return; + if (map.mtexinfos.at(face->texinfo).flags.extended & TEX_EXFLAG_HINT) return; // emit a region @@ -404,7 +408,9 @@ static void GrowNodeRegion(mapentity_t *entity, node_t *node) static void CountFace(mapentity_t *entity, face_t *f, size_t &facesCount, size_t &vertexesCount) { - if (map.mtexinfos.at(f->texinfo).flags.extended & (TEX_EXFLAG_SKIP | TEX_EXFLAG_HINT)) + if (!options.includeSkip && (map.mtexinfos.at(f->texinfo).flags.extended & TEX_EXFLAG_SKIP)) + return; + if (map.mtexinfos.at(f->texinfo).flags.extended & TEX_EXFLAG_HINT) return; if (f->lmshift[1] != 4) diff --git a/qbsp/writebsp.cc b/qbsp/writebsp.cc index 60ba22bc..b7e7ed88 100644 --- a/qbsp/writebsp.cc +++ b/qbsp/writebsp.cc @@ -186,7 +186,8 @@ static void ExportLeaf(mapentity_t *entity, node_t *node) for (face_t **markfaces = node->markfaces; *markfaces; markfaces++) { face_t *face = *markfaces; - if (map.mtexinfos.at(face->texinfo).flags.extended & TEX_EXFLAG_SKIP) + + if (!options.includeSkip && (map.mtexinfos.at(face->texinfo).flags.extended & TEX_EXFLAG_SKIP)) continue; /* emit a marksurface */