diff --git a/common/bspinfo.cc b/common/bspinfo.cc index c78c2197..b15fd524 100644 --- a/common/bspinfo.cc +++ b/common/bspinfo.cc @@ -49,43 +49,32 @@ static std::string hex_string(const uint8_t *bytes, const size_t count) /** * returns a JSON array of models */ -json serialize_bspxbrushlist(const std::vector &lump) +static json serialize_bspxbrushlist(const std::vector &lump) { json j = json::array(); imemstream p(lump.data(), lump.size(), std::ios_base::in | std::ios_base::binary); p >> endianness; + bspxbrushes structured; + p >= structured; - while (true) { - bspxbrushes_permodel src_model; - p >= src_model; - - if (!p) { - break; - } - + for (const bspxbrushes_permodel &src_model : structured.models) { json &model = j.insert(j.end(), json::object()).value(); model["ver"] = src_model.ver; model["modelnum"] = src_model.modelnum; - model["numbrushes"] = src_model.numbrushes; + model["numbrushes"] = src_model.brushes.size(); model["numfaces"] = src_model.numfaces; json &brushes = (model.emplace("brushes", json::array())).first.value(); - for (int32_t i = 0; i < src_model.numbrushes; ++i) { - bspxbrushes_perbrush src_brush; - p >= src_brush; - + for (const bspxbrushes_perbrush &src_brush : src_model.brushes) { json &brush = brushes.insert(brushes.end(), json::object()).value(); brush.push_back({"mins", src_brush.bounds.mins()}); brush.push_back({"maxs", src_brush.bounds.maxs()}); brush.push_back({"contents", src_brush.contents}); json &faces = (brush.emplace("faces", json::array())).first.value(); - for (int32_t j = 0; j < src_brush.numfaces; ++j) { - bspxbrushes_perface src_face; - p >= std::tie(src_face.normal, src_face.dist); - + for (const bspxbrushes_perface &src_face : src_brush.faces) { json &face = faces.insert(faces.end(), json::object()).value(); face.push_back({"normal", src_face.normal}); face.push_back({"dist", src_face.dist}); diff --git a/common/bspxfile.cc b/common/bspxfile.cc index b107380b..4d3dcd4c 100644 --- a/common/bspxfile.cc +++ b/common/bspxfile.cc @@ -50,28 +50,97 @@ void bspx_lump_t::stream_read(std::istream &s) s >= std::tie(lumpname, fileofs, filelen); } -// bspxbrushes_permodel - -void bspxbrushes_permodel::stream_write(std::ostream &s) const -{ - s <= std::tie(ver, modelnum, numbrushes, numfaces); -} - -void bspxbrushes_permodel::stream_read(std::istream &s) -{ - s >= std::tie(ver, modelnum, numbrushes, numfaces); -} - // bspxbrushes_perbrush -void bspxbrushes_perbrush::stream_write(std::ostream &s) const -{ - s <= std::tie(bounds, contents, numfaces); +void bspxbrushes_perbrush::stream_write(std::ostream &s) const { + s <= bounds; + s <= contents; + s <= static_cast(faces.size()); + + for (auto &face : faces) { + s <= face; + } } void bspxbrushes_perbrush::stream_read(std::istream &s) { - s >= std::tie(bounds, contents, numfaces); + s >= bounds; + s >= contents; + + int32_t numfaces = 0; + s >= numfaces; + + faces.resize(numfaces); + + for (auto &face : faces) { + s >= face; + } +} + +// bspxbrushes_permodel + +void bspxbrushes_permodel::stream_write(std::ostream &s) const +{ + s <= ver; + s <= modelnum; + s <= static_cast(brushes.size()); + // count faces (ignore numfaces) + int32_t faces = 0; + for (auto &brush : brushes) { + faces += static_cast(brush.faces.size()); + } + s <= faces; + + // next serialize all of the brushes + for (auto &brush : brushes) { + s <= brush; + } +} + +void bspxbrushes_permodel::stream_read(std::istream &s) +{ + s >= ver; + if (!s) { + // we need to handle end-of-stream due to the bspx lump containing an unknown number + // of bspxbrushes_permodel objects + return; + } + + s >= modelnum; + + int32_t numbrushes; + s >= numbrushes; + s >= numfaces; + + brushes.resize(numbrushes); + for (auto &brush : brushes) { + s >= brush; + } +} + +// bspxbrushes + +void bspxbrushes::stream_write(std::ostream &s) const +{ + for (auto &model : models) { + s <= model; + } +} + +void bspxbrushes::stream_read(std::istream &s) +{ + models.clear(); + + while (true) { + bspxbrushes_permodel model; + s >= model; + + if (!s) { + break; + } + + models.push_back(std::move(model)); + } } // bspxfacenormals_per_vert diff --git a/common/cmdlib.cc b/common/cmdlib.cc index 4beae64b..f90d9b22 100644 --- a/common/cmdlib.cc +++ b/common/cmdlib.cc @@ -728,3 +728,9 @@ void q_aligned_free(void *ptr) free(ptr); #endif } + +std::vector StringToVector(const std::string &str) +{ + std::vector result(str.begin(), str.end()); + return result; +} diff --git a/include/common/bspinfo.hh b/include/common/bspinfo.hh index a06e0e74..1fc1267c 100644 --- a/include/common/bspinfo.hh +++ b/include/common/bspinfo.hh @@ -24,8 +24,6 @@ #include "common/imglib.hh" #include "common/qvec.hh" -#include - #include #include @@ -46,5 +44,3 @@ struct full_atlas_t full_atlas_t build_lightmap_atlas(const mbsp_t &bsp, const bspxentries_t &bspx, const std::vector &litdata, bool use_bspx, bool use_decoupled); void serialize_bsp(const bspdata_t &bspdata, const mbsp_t &bsp, const fs::path &name); - -nlohmann::json serialize_bspxbrushlist(const std::vector &lump); diff --git a/include/common/bspxfile.hh b/include/common/bspxfile.hh index f2947259..9ab10ca8 100644 --- a/include/common/bspxfile.hh +++ b/include/common/bspxfile.hh @@ -53,11 +53,26 @@ struct bspx_lump_t }; // BRUSHLIST BSPX lump +using bspxbrushes_perface = qplane3f; + +struct bspxbrushes_perbrush +{ + aabb3f bounds; + int16_t contents; + // non-axial faces only + std::vector faces; + + // serialize for streams + void stream_write(std::ostream &s) const; + void stream_read(std::istream &s); +}; + struct bspxbrushes_permodel { int32_t ver; int32_t modelnum; - int32_t numbrushes; + std::vector brushes; + // ignored when writing int32_t numfaces; // serialize for streams @@ -65,19 +80,14 @@ struct bspxbrushes_permodel void stream_read(std::istream &s); }; -struct bspxbrushes_perbrush -{ - aabb3f bounds; - int16_t contents; - uint16_t numfaces; +struct bspxbrushes { + std::vector models; // serialize for streams void stream_write(std::ostream &s) const; void stream_read(std::istream &s); }; -using bspxbrushes_perface = qplane3f; - struct bspxfacenormals_per_vert { // these are all indices into bspxfacenormals::normals diff --git a/include/common/cmdlib.hh b/include/common/cmdlib.hh index 4986d1a8..83855c69 100644 --- a/include/common/cmdlib.hh +++ b/include/common/cmdlib.hh @@ -570,3 +570,16 @@ struct omemsizestream : virtual omemsizebuf, std::ostream void CRC_Init(uint16_t &crcvalue); void CRC_ProcessByte(uint16_t &crcvalue, uint8_t data); uint16_t CRC_Block(const uint8_t *start, int count); + +std::vector StringToVector(const std::string &str); + +template +T deserialize(const std::vector &bytes) +{ + auto stream = imemstream(bytes.data(), bytes.size()); + stream >> endianness; + + T result; + stream >= result; + return result; +} diff --git a/include/qbsp/map.hh b/include/qbsp/map.hh index 42464316..e5a55db3 100644 --- a/include/qbsp/map.hh +++ b/include/qbsp/map.hh @@ -338,14 +338,6 @@ size_t EmitFaces(node_t *headnode); void EmitVertices(node_t *headnode); void ExportClipNodes(mapentity_t &entity, node_t *headnode, hull_index_t::value_type hullnum); void ExportDrawNodes(mapentity_t &entity, node_t *headnode, int firstface); - -struct bspxbrushes_s -{ - std::vector lumpdata; -}; -void BSPX_Brushes_Finalize(struct bspxbrushes_s *ctx); -void BSPX_Brushes_Init(struct bspxbrushes_s *ctx); - void WriteBspBrushMap(std::string_view filename_suffix, const bspbrush_t::container &list); bool IsValidTextureProjection(const qvec3f &faceNormal, const qvec3f &s_vec, const qvec3f &t_vec); diff --git a/light/lightgrid.cc b/light/lightgrid.cc index 8f516eff..a829b8bc 100644 --- a/light/lightgrid.cc +++ b/light/lightgrid.cc @@ -34,12 +34,7 @@ #include #include #include - -static std::vector StringToVector(const std::string &str) -{ - std::vector result(str.begin(), str.end()); - return result; -} +#include static aabb3f LightGridBounds(const mbsp_t &bsp) { diff --git a/qbsp/qbsp.cc b/qbsp/qbsp.cc index b286e9bd..4560a69b 100644 --- a/qbsp/qbsp.cc +++ b/qbsp/qbsp.cc @@ -1298,57 +1298,18 @@ static void UpdateEntLump(void) UpdateBSPFileEntitiesLump(); } -/* -Actually writes out the final bspx BRUSHLIST lump -This lump replaces the clipnodes stuff for custom collision sizes. -*/ -void BSPX_Brushes_Finalize(struct bspxbrushes_s *ctx) -{ - // Actually written in WriteBSPFile() - map.exported_bspxbrushes = std::move(ctx->lumpdata); -} -void BSPX_Brushes_Init(struct bspxbrushes_s *ctx) -{ - ctx->lumpdata.clear(); -} - /* WriteBrushes Generates a submodel's direct brush information to a separate file, so the engine doesn't need to depend upon specific hull sizes */ -static void BSPX_Brushes_AddModel(struct bspxbrushes_s *ctx, int modelnum, const std::vector &brushes) +static bspxbrushes_permodel BSPX_Brushes_AddModel(int modelnum, const std::vector &brushes) { - bspxbrushes_permodel permodel{1, modelnum}; + bspxbrushes_permodel permodel{.ver = 1, .modelnum = modelnum}; for (auto &b : brushes) { - permodel.numbrushes++; - for (auto &f : b->faces) { - /*skip axial*/ - const auto &plane = f.get_plane(); - if (plane.get_type() < plane_type_t::PLANE_ANYX) - continue; - permodel.numfaces++; - } - } - - std::ostringstream str(std::ios_base::out | std::ios_base::binary); - - str << endianness; - - str <= permodel; - - for (auto &b : brushes) { - bspxbrushes_perbrush perbrush{}; - - for (auto &f : b->faces) { - /*skip axial*/ - const auto &plane = f.get_plane(); - if (plane.get_type() < plane_type_t::PLANE_ANYX) - continue; - perbrush.numfaces++; - } + bspxbrushes_perbrush &perbrush = permodel.brushes.emplace_back(); perbrush.bounds = b->bounds; @@ -1383,8 +1344,6 @@ static void BSPX_Brushes_AddModel(struct bspxbrushes_s *ctx, int modelnum, const } } - str <= perbrush; - for (auto &f : b->faces) { /*skip axial*/ const auto &plane = f.get_plane(); @@ -1392,24 +1351,21 @@ static void BSPX_Brushes_AddModel(struct bspxbrushes_s *ctx, int modelnum, const continue; bspxbrushes_perface perface = qplane3f(plane.get_normal(), plane.get_dist()); - str <= std::tie(perface.normal, perface.dist); + perbrush.faces.push_back(perface); } } - std::string data = str.str(); - ctx->lumpdata.insert(ctx->lumpdata.end(), (uint8_t *)data.data(), ((uint8_t *)data.data()) + data.size()); + return permodel; } /* for generating BRUSHLIST bspx lump */ -static void BSPX_CreateBrushList(void) +static void BSPX_CreateBrushList() { - struct bspxbrushes_s ctx; + bspxbrushes ctx; if (!qbsp_options.wrbrushes.value()) return; - BSPX_Brushes_Init(&ctx); - for (size_t entnum = 0; entnum < map.entities.size(); ++entnum) { mapentity_t &ent = map.entities.at(entnum); size_t modelnum; @@ -1437,14 +1393,12 @@ static void BSPX_CreateBrushList(void) } if (modelnum == 0) { - for (size_t e = 1; e < map.entities.size(); ++e) { mapentity_t &bent = map.entities.at(e); brushes.reserve(brushes.size() + ent.mapbrushes.size()); if (IsWorldBrushEntity(bent)) { - for (auto &b : bent.mapbrushes) { brushes.push_back(&b); } @@ -1453,11 +1407,15 @@ static void BSPX_CreateBrushList(void) } if (!brushes.empty()) { - BSPX_Brushes_AddModel(&ctx, modelnum, brushes); + ctx.models.push_back(BSPX_Brushes_AddModel(modelnum, brushes)); } } - BSPX_Brushes_Finalize(&ctx); + // Actually written in WriteBSPFile() + std::ostringstream str(std::ios_base::out | std::ios_base::binary); + str << endianness; + str <= ctx; + map.exported_bspxbrushes = StringToVector(str.str()); } /* diff --git a/tests/test_qbsp.cc b/tests/test_qbsp.cc index b19f28d4..680900b4 100644 --- a/tests/test_qbsp.cc +++ b/tests/test_qbsp.cc @@ -67,7 +67,6 @@ mapentity_t &LoadMapPath(const std::filesystem::path &name) } #include -#include std::tuple> LoadTestmap( const std::filesystem::path &name, std::vector extra_args) @@ -1998,21 +1997,14 @@ TEST_CASE("wrbrushes + misc_external_map") { const auto [bsp, bspx, prt] = LoadTestmap("q1_external_map_base.map", {"-wrbrushes"}); - auto models_json = serialize_bspxbrushlist(bspx.at("BRUSHLIST")); - logging::print("{}\n", to_string(models_json)); + bspxbrushes lump = deserialize(bspx.at("BRUSHLIST")); - REQUIRE(models_json.size() == 1); + REQUIRE(lump.models.size() == 1); - auto &model_json = models_json.at(0); - REQUIRE(model_json.at("brushes").size() == 1); + auto &model = lump.models.at(0); + REQUIRE(model.brushes.size() == 1); - auto &brush_json = model_json.at("brushes").at(0); - REQUIRE(brush_json.at("maxs") == nlohmann::json::array({nlohmann::json::number_float_t(64), - nlohmann::json::number_float_t(64), - nlohmann::json::number_float_t(16) - })); - REQUIRE(brush_json.at("mins") == nlohmann::json::array({nlohmann::json::number_float_t(-64), - nlohmann::json::number_float_t(-64), - nlohmann::json::number_float_t(-16) - })); + auto &brush = model.brushes.at(0); + REQUIRE(brush.bounds.maxs() == qvec3f{64,64,16}); + REQUIRE(brush.bounds.mins() == qvec3f{-64,-64,-16}); }