common: structured bspxbrushes reading/writing

This commit is contained in:
Eric Wasylishen 2023-11-25 17:03:22 -07:00
parent de0f66c16e
commit ad9fd7fc59
10 changed files with 150 additions and 130 deletions

View File

@ -49,43 +49,32 @@ static std::string hex_string(const uint8_t *bytes, const size_t count)
/** /**
* returns a JSON array of models * returns a JSON array of models
*/ */
json serialize_bspxbrushlist(const std::vector<uint8_t> &lump) static json serialize_bspxbrushlist(const std::vector<uint8_t> &lump)
{ {
json j = json::array(); json j = json::array();
imemstream p(lump.data(), lump.size(), std::ios_base::in | std::ios_base::binary); imemstream p(lump.data(), lump.size(), std::ios_base::in | std::ios_base::binary);
p >> endianness<std::endian::little>; p >> endianness<std::endian::little>;
bspxbrushes structured;
p >= structured;
while (true) { for (const bspxbrushes_permodel &src_model : structured.models) {
bspxbrushes_permodel src_model;
p >= src_model;
if (!p) {
break;
}
json &model = j.insert(j.end(), json::object()).value(); json &model = j.insert(j.end(), json::object()).value();
model["ver"] = src_model.ver; model["ver"] = src_model.ver;
model["modelnum"] = src_model.modelnum; model["modelnum"] = src_model.modelnum;
model["numbrushes"] = src_model.numbrushes; model["numbrushes"] = src_model.brushes.size();
model["numfaces"] = src_model.numfaces; model["numfaces"] = src_model.numfaces;
json &brushes = (model.emplace("brushes", json::array())).first.value(); json &brushes = (model.emplace("brushes", json::array())).first.value();
for (int32_t i = 0; i < src_model.numbrushes; ++i) { for (const bspxbrushes_perbrush &src_brush : src_model.brushes) {
bspxbrushes_perbrush src_brush;
p >= src_brush;
json &brush = brushes.insert(brushes.end(), json::object()).value(); json &brush = brushes.insert(brushes.end(), json::object()).value();
brush.push_back({"mins", src_brush.bounds.mins()}); brush.push_back({"mins", src_brush.bounds.mins()});
brush.push_back({"maxs", src_brush.bounds.maxs()}); brush.push_back({"maxs", src_brush.bounds.maxs()});
brush.push_back({"contents", src_brush.contents}); brush.push_back({"contents", src_brush.contents});
json &faces = (brush.emplace("faces", json::array())).first.value(); json &faces = (brush.emplace("faces", json::array())).first.value();
for (int32_t j = 0; j < src_brush.numfaces; ++j) { for (const bspxbrushes_perface &src_face : src_brush.faces) {
bspxbrushes_perface src_face;
p >= std::tie(src_face.normal, src_face.dist);
json &face = faces.insert(faces.end(), json::object()).value(); json &face = faces.insert(faces.end(), json::object()).value();
face.push_back({"normal", src_face.normal}); face.push_back({"normal", src_face.normal});
face.push_back({"dist", src_face.dist}); face.push_back({"dist", src_face.dist});

View File

@ -50,28 +50,97 @@ void bspx_lump_t::stream_read(std::istream &s)
s >= std::tie(lumpname, fileofs, filelen); 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 // bspxbrushes_perbrush
void bspxbrushes_perbrush::stream_write(std::ostream &s) const void bspxbrushes_perbrush::stream_write(std::ostream &s) const {
{ s <= bounds;
s <= std::tie(bounds, contents, numfaces); s <= contents;
s <= static_cast<int32_t>(faces.size());
for (auto &face : faces) {
s <= face;
}
} }
void bspxbrushes_perbrush::stream_read(std::istream &s) 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<int32_t>(brushes.size());
// count faces (ignore numfaces)
int32_t faces = 0;
for (auto &brush : brushes) {
faces += static_cast<int32_t>(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 // bspxfacenormals_per_vert

View File

@ -728,3 +728,9 @@ void q_aligned_free(void *ptr)
free(ptr); free(ptr);
#endif #endif
} }
std::vector<uint8_t> StringToVector(const std::string &str)
{
std::vector<uint8_t> result(str.begin(), str.end());
return result;
}

View File

@ -24,8 +24,6 @@
#include "common/imglib.hh" #include "common/imglib.hh"
#include "common/qvec.hh" #include "common/qvec.hh"
#include <nlohmann/json_fwd.hpp>
#include <map> #include <map>
#include <vector> #include <vector>
@ -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<uint8_t> &litdata, bool use_bspx, bool use_decoupled); full_atlas_t build_lightmap_atlas(const mbsp_t &bsp, const bspxentries_t &bspx, const std::vector<uint8_t> &litdata, bool use_bspx, bool use_decoupled);
void serialize_bsp(const bspdata_t &bspdata, const mbsp_t &bsp, const fs::path &name); void serialize_bsp(const bspdata_t &bspdata, const mbsp_t &bsp, const fs::path &name);
nlohmann::json serialize_bspxbrushlist(const std::vector<uint8_t> &lump);

View File

@ -53,11 +53,26 @@ struct bspx_lump_t
}; };
// BRUSHLIST BSPX lump // BRUSHLIST BSPX lump
using bspxbrushes_perface = qplane3f;
struct bspxbrushes_perbrush
{
aabb3f bounds;
int16_t contents;
// non-axial faces only
std::vector<bspxbrushes_perface> faces;
// serialize for streams
void stream_write(std::ostream &s) const;
void stream_read(std::istream &s);
};
struct bspxbrushes_permodel struct bspxbrushes_permodel
{ {
int32_t ver; int32_t ver;
int32_t modelnum; int32_t modelnum;
int32_t numbrushes; std::vector<bspxbrushes_perbrush> brushes;
// ignored when writing
int32_t numfaces; int32_t numfaces;
// serialize for streams // serialize for streams
@ -65,19 +80,14 @@ struct bspxbrushes_permodel
void stream_read(std::istream &s); void stream_read(std::istream &s);
}; };
struct bspxbrushes_perbrush struct bspxbrushes {
{ std::vector<bspxbrushes_permodel> models;
aabb3f bounds;
int16_t contents;
uint16_t numfaces;
// serialize for streams // serialize for streams
void stream_write(std::ostream &s) const; void stream_write(std::ostream &s) const;
void stream_read(std::istream &s); void stream_read(std::istream &s);
}; };
using bspxbrushes_perface = qplane3f;
struct bspxfacenormals_per_vert struct bspxfacenormals_per_vert
{ {
// these are all indices into bspxfacenormals::normals // these are all indices into bspxfacenormals::normals

View File

@ -570,3 +570,16 @@ struct omemsizestream : virtual omemsizebuf, std::ostream
void CRC_Init(uint16_t &crcvalue); void CRC_Init(uint16_t &crcvalue);
void CRC_ProcessByte(uint16_t &crcvalue, uint8_t data); void CRC_ProcessByte(uint16_t &crcvalue, uint8_t data);
uint16_t CRC_Block(const uint8_t *start, int count); uint16_t CRC_Block(const uint8_t *start, int count);
std::vector<uint8_t> StringToVector(const std::string &str);
template <class T>
T deserialize(const std::vector<uint8_t> &bytes)
{
auto stream = imemstream(bytes.data(), bytes.size());
stream >> endianness<std::endian::little>;
T result;
stream >= result;
return result;
}

View File

@ -338,14 +338,6 @@ size_t EmitFaces(node_t *headnode);
void EmitVertices(node_t *headnode); void EmitVertices(node_t *headnode);
void ExportClipNodes(mapentity_t &entity, node_t *headnode, hull_index_t::value_type hullnum); void ExportClipNodes(mapentity_t &entity, node_t *headnode, hull_index_t::value_type hullnum);
void ExportDrawNodes(mapentity_t &entity, node_t *headnode, int firstface); void ExportDrawNodes(mapentity_t &entity, node_t *headnode, int firstface);
struct bspxbrushes_s
{
std::vector<uint8_t> 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); 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); bool IsValidTextureProjection(const qvec3f &faceNormal, const qvec3f &s_vec, const qvec3f &t_vec);

View File

@ -34,12 +34,7 @@
#include <common/prtfile.hh> #include <common/prtfile.hh>
#include <common/parallel.hh> #include <common/parallel.hh>
#include <common/qvec.hh> #include <common/qvec.hh>
#include <common/cmdlib.hh>
static std::vector<uint8_t> StringToVector(const std::string &str)
{
std::vector<uint8_t> result(str.begin(), str.end());
return result;
}
static aabb3f LightGridBounds(const mbsp_t &bsp) static aabb3f LightGridBounds(const mbsp_t &bsp)
{ {

View File

@ -1298,57 +1298,18 @@ static void UpdateEntLump(void)
UpdateBSPFileEntitiesLump(); 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 WriteBrushes
Generates a submodel's direct brush information to a separate file, so the engine doesn't need to depend upon specific Generates a submodel's direct brush information to a separate file, so the engine doesn't need to depend upon specific
hull sizes hull sizes
*/ */
static void BSPX_Brushes_AddModel(struct bspxbrushes_s *ctx, int modelnum, const std::vector<mapbrush_t *> &brushes) static bspxbrushes_permodel BSPX_Brushes_AddModel(int modelnum, const std::vector<mapbrush_t *> &brushes)
{ {
bspxbrushes_permodel permodel{1, modelnum}; bspxbrushes_permodel permodel{.ver = 1, .modelnum = modelnum};
for (auto &b : brushes) { for (auto &b : brushes) {
permodel.numbrushes++; bspxbrushes_perbrush &perbrush = permodel.brushes.emplace_back();
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<std::endian::little>;
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++;
}
perbrush.bounds = b->bounds; 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) { for (auto &f : b->faces) {
/*skip axial*/ /*skip axial*/
const auto &plane = f.get_plane(); const auto &plane = f.get_plane();
@ -1392,24 +1351,21 @@ static void BSPX_Brushes_AddModel(struct bspxbrushes_s *ctx, int modelnum, const
continue; continue;
bspxbrushes_perface perface = qplane3f(plane.get_normal(), plane.get_dist()); 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(); return permodel;
ctx->lumpdata.insert(ctx->lumpdata.end(), (uint8_t *)data.data(), ((uint8_t *)data.data()) + data.size());
} }
/* for generating BRUSHLIST bspx lump */ /* 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()) if (!qbsp_options.wrbrushes.value())
return; return;
BSPX_Brushes_Init(&ctx);
for (size_t entnum = 0; entnum < map.entities.size(); ++entnum) { for (size_t entnum = 0; entnum < map.entities.size(); ++entnum) {
mapentity_t &ent = map.entities.at(entnum); mapentity_t &ent = map.entities.at(entnum);
size_t modelnum; size_t modelnum;
@ -1437,14 +1393,12 @@ static void BSPX_CreateBrushList(void)
} }
if (modelnum == 0) { if (modelnum == 0) {
for (size_t e = 1; e < map.entities.size(); ++e) { for (size_t e = 1; e < map.entities.size(); ++e) {
mapentity_t &bent = map.entities.at(e); mapentity_t &bent = map.entities.at(e);
brushes.reserve(brushes.size() + ent.mapbrushes.size()); brushes.reserve(brushes.size() + ent.mapbrushes.size());
if (IsWorldBrushEntity(bent)) { if (IsWorldBrushEntity(bent)) {
for (auto &b : bent.mapbrushes) { for (auto &b : bent.mapbrushes) {
brushes.push_back(&b); brushes.push_back(&b);
} }
@ -1453,11 +1407,15 @@ static void BSPX_CreateBrushList(void)
} }
if (!brushes.empty()) { 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<std::endian::little>;
str <= ctx;
map.exported_bspxbrushes = StringToVector(str.str());
} }
/* /*

View File

@ -67,7 +67,6 @@ mapentity_t &LoadMapPath(const std::filesystem::path &name)
} }
#include <common/bspinfo.hh> #include <common/bspinfo.hh>
#include <nlohmann/json.hpp>
std::tuple<mbsp_t, bspxentries_t, std::optional<prtfile_t>> LoadTestmap( std::tuple<mbsp_t, bspxentries_t, std::optional<prtfile_t>> LoadTestmap(
const std::filesystem::path &name, std::vector<std::string> extra_args) const std::filesystem::path &name, std::vector<std::string> extra_args)
@ -1998,21 +1997,14 @@ TEST_CASE("wrbrushes + misc_external_map")
{ {
const auto [bsp, bspx, prt] = LoadTestmap("q1_external_map_base.map", {"-wrbrushes"}); const auto [bsp, bspx, prt] = LoadTestmap("q1_external_map_base.map", {"-wrbrushes"});
auto models_json = serialize_bspxbrushlist(bspx.at("BRUSHLIST")); bspxbrushes lump = deserialize<bspxbrushes>(bspx.at("BRUSHLIST"));
logging::print("{}\n", to_string(models_json));
REQUIRE(models_json.size() == 1); REQUIRE(lump.models.size() == 1);
auto &model_json = models_json.at(0); auto &model = lump.models.at(0);
REQUIRE(model_json.at("brushes").size() == 1); REQUIRE(model.brushes.size() == 1);
auto &brush_json = model_json.at("brushes").at(0); auto &brush = model.brushes.at(0);
REQUIRE(brush_json.at("maxs") == nlohmann::json::array({nlohmann::json::number_float_t(64), REQUIRE(brush.bounds.maxs() == qvec3f{64,64,16});
nlohmann::json::number_float_t(64), REQUIRE(brush.bounds.mins() == qvec3f{-64,-64,-16});
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)
}));
} }