track a shared pointer for `map_source_location` instead of memory pointer, so that we can allocate it as we go

use it for brushes and faces
This commit is contained in:
Jonathan 2022-08-04 15:37:37 -04:00
parent 9af74ce083
commit 627428756c
5 changed files with 89 additions and 45 deletions

View File

@ -48,29 +48,61 @@ struct map_source_location
// to be. note that because the locations only live for the lifetime
// of the object it is belonging to, whatever this string
// points to must out-live the object.
std::string_view source_name;
std::shared_ptr<std::string> source_name = nullptr;
// the line number that this location is associated to, if any. Synthetic
// locations may not necessarily have an associated line number.
std::optional<size_t> line_number;
std::optional<size_t> line_number = std::nullopt;
// reference to a location of the object that derived us. this is mainly
// for synthetic locations; ie a bspbrush_t's sides aren't themselves generated
// by a source or line, but they are derived from a mapbrush_t which does have
// a location. The object it points to must outlive this object.
const std::optional<std::reference_wrapper<map_source_location>> derivative;
// a location. The object it points to must outlive this object. this is mainly
// for debugging.
std::optional<std::reference_wrapper<const map_source_location>> derivative = std::nullopt;
explicit operator bool() const { return source_name != nullptr; }
// return a modified source location with only the line changeed
inline map_source_location on_line(size_t new_line) const
{
return { source_name, new_line, derivative };
}
// if we update to C++20 we could use this to track where location objects come from:
// std::source_location created_location;
};
// FMT support
template<>
struct fmt::formatter<map_source_location>
{
constexpr auto parse(format_parse_context &ctx) -> decltype(ctx.begin()) { return ctx.end(); }
template<typename FormatContext>
auto format(const map_source_location &v, FormatContext &ctx) -> decltype(ctx.out())
{
if (v.source_name) {
format_to(ctx.out(), "{}", *v.source_name.get());
} else {
format_to(ctx.out(), "unknown/unset location");
}
if (v.line_number.has_value()) {
format_to(ctx.out(), "[line {}]", v.line_number.value());
}
return ctx.out();
}
};
struct mapface_t
{
size_t planenum;
std::array<qvec3d, 3> planepts{};
std::string texname{};
int texinfo = 0;
int linenum = 0;
map_source_location line;
bool bevel = false;
bool visible = false;
winding_t winding; // winding used to calculate bevels
@ -105,7 +137,7 @@ public:
brushformat_t format = brushformat_t::NORMAL;
aabb3d bounds {};
std::optional<uint32_t> outputnumber; /* only set for original brushes */
size_t entitynum = 0, linenum = 0;
map_source_location line;
};
struct lumpdata
@ -370,7 +402,7 @@ extern mapdata_t map;
void CalculateWorldExtent(void);
bool ParseEntity(parser_t &parser, mapentity_t *entity);
bool ParseEntity(parser_t &parser, mapentity_t *entity, const map_source_location &map_source);
void ProcessExternalMapEntity(mapentity_t *entity);
void ProcessAreaPortal(mapentity_t *entity);

View File

@ -80,11 +80,11 @@ static void CheckFace(side_t *face, const mapface_t &sourceface)
if (face->w.size() < 3) {
if (face->w.size() == 2) {
logging::print(
"WARNING: line {}: partially clipped into degenerate polygon @ ({}) - ({})\n", sourceface.linenum, face->w[0], face->w[1]);
"WARNING: {}: partially clipped into degenerate polygon @ ({}) - ({})\n", sourceface.line, face->w[0], face->w[1]);
} else if (face->w.size() == 1) {
logging::print("WARNING: line {}: partially clipped into degenerate polygon @ ({})\n", sourceface.linenum, face->w[0]);
logging::print("WARNING: {}: partially clipped into degenerate polygon @ ({})\n", sourceface.line, face->w[0]);
} else {
logging::print("WARNING: line {}: completely clipped away\n", sourceface.linenum);
logging::print("WARNING: {}: completely clipped away\n", sourceface.line);
}
face->w.clear();
@ -101,7 +101,7 @@ static void CheckFace(side_t *face, const mapface_t &sourceface)
for (auto &v : p1) {
if (fabs(v) > qbsp_options.worldextent.value()) {
// this is fatal because a point should never lay outside the world
FError("line {}: coordinate out of range ({})\n", sourceface.linenum, v);
FError("{}: coordinate out of range ({})\n", sourceface.line, v);
}
}
@ -109,7 +109,7 @@ static void CheckFace(side_t *face, const mapface_t &sourceface)
{
vec_t dist = face->get_plane().distance_to(p1);
if (fabs(dist) > qbsp_options.epsilon.value()) {
logging::print("WARNING: Line {}: Point ({:.3} {:.3} {:.3}) off plane by {:2.4}\n", sourceface.linenum,
logging::print("WARNING: {}: Point ({:.3} {:.3} {:.3}) off plane by {:2.4}\n", sourceface.line,
p1[0], p1[1], p1[2], dist);
}
}
@ -118,8 +118,8 @@ static void CheckFace(side_t *face, const mapface_t &sourceface)
qvec3d edgevec = p2 - p1;
vec_t length = qv::length(edgevec);
if (length < qbsp_options.epsilon.value()) {
logging::print("WARNING: Line {}: Healing degenerate edge ({}) at ({:.3f} {:.3} {:.3})\n",
sourceface.linenum, length, p1[0], p1[1], p1[2]);
logging::print("WARNING: {}: Healing degenerate edge ({}) at ({:.3f} {:.3} {:.3})\n",
sourceface.line, length, p1[0], p1[1], p1[2]);
for (size_t j = i + 1; j < face->w.size(); j++)
face->w[j - 1] = face->w[j];
face->w.resize(face->w.size() - 1);
@ -137,8 +137,8 @@ static void CheckFace(side_t *face, const mapface_t &sourceface)
continue;
vec_t dist = qv::dot(face->w[j], edgenormal);
if (dist > edgedist) {
logging::print("WARNING: line {}: Found a non-convex face (error size {}, point: {})\n",
sourceface.linenum, dist - edgedist, face->w[j]);
logging::print("WARNING: {}: Found a non-convex face (error size {}, point: {})\n",
sourceface.line, dist - edgedist, face->w[j]);
face->w.clear();
return;
}
@ -252,9 +252,8 @@ contentflags_t Brush_GetContents(const mapbrush_t *mapbrush)
}
if (!contents.types_equal(base_contents, qbsp_options.target_game)) {
logging::print("mixed face contents ({} != {}) at line {}\n",
base_contents.to_string(qbsp_options.target_game), contents.to_string(qbsp_options.target_game),
mapface.linenum);
logging::print("WARNING: {}: mixed face contents ({} != {})\n",
mapface.line, base_contents.to_string(qbsp_options.target_game), contents.to_string(qbsp_options.target_game));
break;
}
}
@ -629,11 +628,12 @@ void bspbrush_t::update_bounds()
}
for (size_t i = 0; i < 3; i++) {
// todo: map_source_location in bspbrush_t
if (this->bounds.mins()[0] <= -qbsp_options.worldextent.value() || this->bounds.maxs()[0] >= qbsp_options.worldextent.value()) {
logging::print("WARNING: line {}: brush bounds out of range\n", mapbrush->linenum);
logging::print("WARNING: {}: brush bounds out of range\n", mapbrush ? mapbrush->line : map_source_location());
}
if (this->bounds.mins()[0] >= qbsp_options.worldextent.value() || this->bounds.maxs()[0] <= -qbsp_options.worldextent.value()) {
logging::print("WARNING: line {}: no visible sides on brush\n", mapbrush->linenum);
logging::print("WARNING: {}: no visible sides on brush\n", mapbrush ? mapbrush->line : map_source_location());
}
}

View File

@ -1072,10 +1072,12 @@ static std::unique_ptr<tree_t> BrushBSP(mapentity_t *entity, std::vector<std::un
for (const auto &b : brushlist) {
c_brushes++;
// fixme-brushbsp: why does this just print and do nothing? should
// the brush be removed?
double volume = BrushVolume(*b);
if (volume < qbsp_options.microvolume.value()) {
logging::print("WARNING: line {}: microbrush\n",
b->original->mapbrush->linenum);
logging::print("WARNING: {}: microbrush\n",
b->original->mapbrush->line);
}
for (side_t &side : b->sides) {

View File

@ -1475,14 +1475,14 @@ static void ParseTextureDef(parser_t &parser, mapface_t &mapface, const mapbrush
if (!(extinfo.info->flags.native & (Q2_SURF_TRANS33 | Q2_SURF_TRANS66))) {
extinfo.info->contents.native |= Q2_CONTENTS_DETAIL;
logging::print("WARNING: face at line {}: swapped TRANSLUCENT for DETAIL\n", mapface.linenum);
logging::print("WARNING: {}: swapped TRANSLUCENT for DETAIL\n", mapface.line);
}
}
// This fixes a bug in some old maps.
if ((extinfo.info->flags.native & (Q2_SURF_SKY | Q2_SURF_NODRAW)) == (Q2_SURF_SKY | Q2_SURF_NODRAW)) {
extinfo.info->flags.native &= ~Q2_SURF_NODRAW;
logging::print("WARNING: face at line {}: SKY | NODRAW mixed. Removing NODRAW.\n", mapface.linenum);
logging::print("WARNING: {}: SKY | NODRAW mixed. Removing NODRAW.\n", mapface.line);
}
}
@ -1497,7 +1497,7 @@ static void ParseTextureDef(parser_t &parser, mapface_t &mapface, const mapbrush
if (!contents.is_valid(qbsp_options.target_game, false)) {
auto old_contents = contents;
qbsp_options.target_game->contents_make_valid(contents);
logging::print("WARNING: line {}: face has invalid contents {}, remapped to {}\n", mapface.linenum,
logging::print("WARNING: {}: face has invalid contents {}, remapped to {}\n", mapface.line,
old_contents.to_string(qbsp_options.target_game), contents.to_string(qbsp_options.target_game));
}
@ -1581,8 +1581,8 @@ inline bool IsValidTextureProjection(const mapface_t &mapface, const maptexinfo_
static void ValidateTextureProjection(mapface_t &mapface, maptexinfo_t *tx)
{
if (!IsValidTextureProjection(mapface, tx)) {
logging::print("WARNING: repairing invalid texture projection on line {} (\"{}\" near {} {} {})\n",
mapface.linenum, mapface.texname, (int)mapface.planepts[0][0], (int)mapface.planepts[0][1],
logging::print("WARNING: {}: repairing invalid texture projection (\"{}\" near {} {} {})\n",
mapface.line, mapface.texname, (int)mapface.planepts[0][0], (int)mapface.planepts[0][1],
(int)mapface.planepts[0][2]);
// Reset texturing to sensible defaults
@ -1603,7 +1603,8 @@ static std::optional<mapface_t> ParseBrushFace(parser_t &parser, const mapbrush_
int i, j;
mapface_t face;
face.linenum = parser.linenum;
face.line = brush.line.on_line(parser.linenum);
ParsePlaneDef(parser, planepts);
normal_ok = face.set_planepts(planepts);
@ -1789,13 +1790,13 @@ inline void AddBrushBevels(mapentity_t &e, mapbrush_t &b)
}
}
static mapbrush_t ParseBrush(parser_t &parser, mapentity_t &entity)
static mapbrush_t ParseBrush(parser_t &parser, mapentity_t &entity, const map_source_location &entity_source)
{
mapbrush_t brush;
// ericw -- brush primitives
if (!parser.parse_token(PARSE_PEEK))
FError("Unexpected EOF after { beginning brush");
FError("{}: unexpected EOF after { beginning brush", entity_source);
if (parser.token == "(") {
brush.format = brushformat_t::NORMAL;
@ -1818,8 +1819,8 @@ static mapbrush_t ParseBrush(parser_t &parser, mapentity_t &entity)
while (parser.parse_token()) {
// set linenum after first parsed token
if (!brush.linenum) {
brush.linenum = parser.linenum;
if (!brush.line) {
brush.line = entity_source.on_line(parser.linenum);
}
if (parser.token == "}")
@ -1866,13 +1867,16 @@ static mapbrush_t ParseBrush(parser_t &parser, mapentity_t &entity)
return brush;
}
bool ParseEntity(parser_t &parser, mapentity_t *entity)
bool ParseEntity(parser_t &parser, mapentity_t *entity, const map_source_location &map_source)
{
if (!parser.parse_token())
return false;
if (parser.token != "{")
FError("line {}: Invalid entity format, { not found", parser.linenum);
map_source_location entity_source = map_source.on_line(parser.linenum);
if (parser.token != "{") {
FError("{}: Invalid entity format, { not found", entity_source);
}
entity->mapbrushes.clear();
@ -1885,7 +1889,7 @@ bool ParseEntity(parser_t &parser, mapentity_t *entity)
// once we run into the first brush, set up textures state.
EnsureTexturesLoaded();
entity->mapbrushes.emplace_back(ParseBrush(parser, *entity));
entity->mapbrushes.emplace_back(ParseBrush(parser, *entity, entity_source));
} else {
ParseEpair(parser, entity);
}
@ -2007,9 +2011,10 @@ static mapentity_t LoadExternalMap(const std::string &filename)
}
parser_t parser(file->data(), file->size());
map_source_location entity_source { std::make_shared<std::string>(filename), parser.linenum };
// parse the worldspawn
if (!ParseEntity(parser, &dest)) {
if (!ParseEntity(parser, &dest, entity_source)) {
FError("'{}': Couldn't parse worldspawn entity\n", filename);
}
const std::string &classname = dest.epairs.get("classname");
@ -2019,7 +2024,7 @@ static mapentity_t LoadExternalMap(const std::string &filename)
// parse any subsequent entities, move any brushes to worldspawn
mapentity_t dummy{};
while (ParseEntity(parser, &dummy)) {
while (ParseEntity(parser, &dummy, entity_source = entity_source.on_line(parser.linenum))) {
// move the brushes to the worldspawn
dest.mapbrushes.insert(dest.mapbrushes.end(), std::make_move_iterator(dummy.mapbrushes.begin()), std::make_move_iterator(dummy.mapbrushes.end()));
@ -2198,10 +2203,10 @@ inline void CalculateBrushBounds(mapbrush_t &ob)
for (size_t i = 0; i < 3; i++) {
if (ob.bounds.mins()[0] <= -qbsp_options.worldextent.value() || ob.bounds.maxs()[0] >= qbsp_options.worldextent.value()) {
logging::print("WARNING: line {}: brush bounds out of range\n", ob.linenum);
logging::print("WARNING: {}: brush bounds out of range\n", ob.line);
}
if (ob.bounds.mins()[0] >= qbsp_options.worldextent.value() || ob.bounds.maxs()[0] <= -qbsp_options.worldextent.value()) {
logging::print("WARNING: line {}: no visible sides on brush\n", ob.linenum);
logging::print("WARNING: {}: no visible sides on brush\n", ob.line);
}
}
}
@ -2239,7 +2244,8 @@ void ProcessMapBrushes()
if (&entity == map.world_entity()) {
logging::print("WARNING: Ignoring origin brush in worldspawn\n");
} else if (entity.epairs.has("origin")) {
logging::print("WARNING: Entity at line {} has multiple origin brushes\n", entity.mapbrushes.front().faces[0].linenum);
// fixme-brushbsp: entity.line
logging::print("WARNING: Entity at {} has multiple origin brushes\n", entity.mapbrushes.front().faces[0].line);
} else {
entity.origin = brush.bounds.centroid();
entity.epairs.set("origin", qv::to_string(entity.origin));
@ -2350,11 +2356,12 @@ void LoadMapFile(void)
}
parser_t parser(file->data(), file->size());
map_source_location entity_source { std::make_shared<std::string>(qbsp_options.map_path.string()) };
for (int i = 0;; i++) {
mapentity_t &entity = map.entities.emplace_back();
if (!ParseEntity(parser, &entity)) {
if (!ParseEntity(parser, &entity, entity_source.on_line(parser.linenum))) {
break;
}
}
@ -2375,11 +2382,12 @@ void LoadMapFile(void)
}
parser_t parser(file->data(), file->size());
map_source_location entity_source { std::make_shared<std::string>(qbsp_options.add.value()) };
for (int i = 0;; i++) {
mapentity_t &entity = map.entities.emplace_back();
if (!ParseEntity(parser, &entity)) {
if (!ParseEntity(parser, &entity, entity_source.on_line(parser.linenum))) {
break;
}

View File

@ -44,10 +44,12 @@ static mapentity_t LoadMap(const char *map)
// FIXME: ???
mapentity_t &entity = ::map.entities.emplace_back();
map_source_location entity_source { std::make_shared<std::string>(Catch::getResultCapture().getCurrentTestName()), 0 };
mapentity_t worldspawn;
// FIXME: adds the brush to the global map...
Q_assert(ParseEntity(parser, &worldspawn));
Q_assert(ParseEntity(parser, &worldspawn, entity_source));
CalculateWorldExtent();