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
*/
json serialize_bspxbrushlist(const std::vector<uint8_t> &lump)
static json serialize_bspxbrushlist(const std::vector<uint8_t> &lump)
{
json j = json::array();
imemstream p(lump.data(), lump.size(), std::ios_base::in | std::ios_base::binary);
p >> endianness<std::endian::little>;
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});

View File

@ -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<int32_t>(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<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

View File

@ -728,3 +728,9 @@ void q_aligned_free(void *ptr)
free(ptr);
#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/qvec.hh"
#include <nlohmann/json_fwd.hpp>
#include <map>
#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);
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
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
{
int32_t ver;
int32_t modelnum;
int32_t numbrushes;
std::vector<bspxbrushes_perbrush> 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<bspxbrushes_permodel> 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

View File

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

View File

@ -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<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) {
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<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++;
}
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<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 <nlohmann/json.hpp>
std::tuple<mbsp_t, bspxentries_t, std::optional<prtfile_t>> LoadTestmap(
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"});
auto models_json = serialize_bspxbrushlist(bspx.at("BRUSHLIST"));
logging::print("{}\n", to_string(models_json));
bspxbrushes lump = deserialize<bspxbrushes>(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});
}