move in the rest of the missing code back into LoadBrush

fix bug with c_brushesremoved brushes
move & CreateBrushWindings use where required
store the source face we made a bspbrush_t side from
fix test issue
This commit is contained in:
Jonathan 2022-08-03 12:35:47 -04:00
parent 5da88122a5
commit ec62a0e8d0
7 changed files with 142 additions and 116 deletions

View File

@ -29,6 +29,7 @@
class mapentity_t;
struct maptexinfo_t;
struct mapface_t;
struct side_t
{
@ -43,6 +44,7 @@ struct side_t
// non-visible means we can discard the brush side
// (avoiding generating a BSP spit, so expanding it outwards)
bool bevel; // don't ever use for bsp splitting
const mapface_t *source; // the mapface we were generated from
bool tested;
@ -82,14 +84,7 @@ struct bspbrush_t
qplane3d Face_Plane(const face_t *face);
qplane3d Face_Plane(const side_t *face);
enum class rotation_t
{
none,
hipnotic,
origin_brush
};
std::optional<bspbrush_t> LoadBrush(const mapentity_t *src, const mapbrush_t *mapbrush, const contentflags_t &contents,
const int hullnum);
bspbrush_t LoadBrush(const mapentity_t *src, const mapbrush_t *mapbrush, const contentflags_t &contents, const int hullnum);
contentflags_t Brush_GetContents(const mapbrush_t *mapbrush);
void CreateBrushWindings(bspbrush_t *brush);
void FreeBrushes(mapentity_t *ent);

View File

@ -45,6 +45,7 @@ struct mapface_t
int texinfo = 0;
int linenum = 0;
bool bevel = false;
winding_t winding; // winding used to calculate bevels
surfflags_t flags{};
@ -55,8 +56,6 @@ struct mapface_t
// for convert
std::optional<extended_texinfo_t> raw_info;
winding_t winding;
bool set_planepts(const std::array<qvec3d, 3> &pts);
const texvecf &get_texvecs() const;
@ -87,6 +86,13 @@ struct lumpdata
void *data;
};
enum class rotation_t
{
none,
hipnotic,
origin_brush
};
class mapentity_t
{
public:
@ -100,6 +106,7 @@ public:
#endif
qvec3d origin{};
rotation_t rotation;
std::list<mapbrush_t> mapbrushes;

View File

@ -321,6 +321,40 @@ contentflags_t Brush_GetContents(const mapbrush_t *mapbrush)
return base_contents;
}
/*
==================
CreateBrushWindings
Create all of the windings for the specified brush, and
calculate its bounds.
==================
*/
void CreateBrushWindings(bspbrush_t *brush)
{
std::optional<winding_t> w;
for (int i = 0; i < brush->sides.size(); i++) {
side_t *side = &brush->sides[i];
w = BaseWindingForPlane(Face_Plane(side));
for (int j = 0; j < brush->sides.size() && w; j++) {
if (i == j)
continue;
if (brush->sides[j].bevel)
continue;
qplane3d plane = -Face_Plane(&brush->sides[j]);
w = w->clip(plane, 0, false)[SIDE_FRONT]; // CLIP_EPSILON);
}
if (w) {
side->w = *w;
} else {
side->w.clear();
}
}
brush->update_bounds();
}
/*
===============
LoadBrush
@ -328,18 +362,20 @@ LoadBrush
Converts a mapbrush to a bsp brush
===============
*/
std::optional<bspbrush_t> LoadBrush(const mapentity_t *src, const mapbrush_t *mapbrush, const contentflags_t &contents,
bspbrush_t LoadBrush(const mapentity_t *src, const mapbrush_t *mapbrush, const contentflags_t &contents,
const int hullnum)
{
// create the brush
bspbrush_t brush{};
brush.contents = contents;
brush.sides.reserve(mapbrush->faces.size());
brush.mapbrush = mapbrush;
for (size_t i = 0; i < mapbrush->faces.size(); i++) {
auto &src = mapbrush->faces[i];
if (src.bevel) {
// don't add bevels for the point hull
if (hullnum <= 0 && src.bevel) {
continue;
}
@ -348,26 +384,73 @@ std::optional<bspbrush_t> LoadBrush(const mapentity_t *src, const mapbrush_t *ma
dst.texinfo = hullnum > 0 ? 0 : src.texinfo;
dst.planenum = src.planenum;
dst.bevel = src.bevel;
// TEMP
dst.w = src.winding;
CheckFace(&dst, src);
dst.source = &src;
}
// todo: expand planes, recalculate bounds & windings
brush.bounds = mapbrush->bounds;
#if 0
// expand the brushes for the hull
if (hullnum > 0) {
auto &hulls = qbsp_options.target_game->get_hull_sizes();
Q_assert(hullnum < hulls.size());
ExpandBrush(&hullbrush, *(hulls.begin() + hullnum), facelist);
facelist = CreateBrushFaces(src, &hullbrush, hullnum);
}
#endif
auto &hull = *(hulls.begin() + hullnum);
for (auto &mapface : brush.sides) {
if (mapface.get_texinfo().flags.no_expand) {
continue;
}
qvec3d corner{};
for (int32_t x = 0; x < 3; x++) {
if (mapface.get_plane().get_normal()[x] > 0) {
corner[x] = hull[1][x];
} else if (mapface.get_plane().get_normal()[x] < 0) {
corner[x] = hull[0][x];
}
}
qplane3d plane = mapface.get_plane();
plane.dist += qv::dot(corner, plane.normal);
mapface.planenum = map.add_or_find_plane(plane);
mapface.bevel = false;
}
}
CreateBrushWindings(&brush);
for (auto &face : brush.sides) {
CheckFace(&face, *face.source);
}
// Rotatable objects must have a bounding box big enough to
// account for all its rotations
// if -wrbrushes is in use, don't do this for the clipping hulls because it depends on having
// the actual non-hacked bbox (it doesn't write axial planes).
// Hexen2 also doesn't want the bbox expansion, it's handled in engine (see: SV_LinkEdict)
// Only do this for hipnotic rotation. For origin brushes in Quake, it breaks some of their
// uses (e.g. func_train). This means it's up to the mapper to expand the model bounds with
// clip brushes if they're going to rotate a model in vanilla Quake and not use hipnotic rotation.
// The idea behind the bounds expansion was to avoid incorrect vis culling (AFAIK).
const bool shouldExpand = (src->origin[0] != 0.0 || src->origin[1] != 0.0 || src->origin[2] != 0.0) &&
src->rotation == rotation_t::hipnotic &&
(hullnum >= 0) // hullnum < 0 corresponds to -wrbrushes clipping hulls
&& qbsp_options.target_game->id != GAME_HEXEN_II; // never do this in Hexen 2
if (shouldExpand) {
vec_t max = -std::numeric_limits<vec_t>::infinity(), min = std::numeric_limits<vec_t>::infinity();
for (auto &v : brush.bounds.mins()) {
min = ::min(min, v);
max = ::max(max, v);
}
for (auto &v : brush.bounds.maxs()) {
min = ::min(min, v);
max = ::max(max, v);
}
vec_t delta = std::max(fabs(max), fabs(min));
brush.bounds = {-delta, delta};
}
brush.mapbrush = mapbrush;
return brush;
}
@ -483,12 +566,8 @@ static void Brush_LoadEntity(mapentity_t *dst, const mapentity_t *src, const int
*/
if (hullnum != HULL_COLLISION && contents.is_clip(qbsp_options.target_game)) {
if (hullnum == 0) {
std::optional<bspbrush_t> brush = LoadBrush(src, &mapbrush, contents, hullnum);
if (brush) {
dst->bounds += brush->bounds;
}
bspbrush_t brush = LoadBrush(src, &mapbrush, contents, hullnum);
dst->bounds += brush.bounds;
continue;
// for hull1, 2, etc., convert clip to CONTENTS_SOLID
} else {
@ -533,22 +612,21 @@ static void Brush_LoadEntity(mapentity_t *dst, const mapentity_t *src, const int
contents.set_clips_same_type(clipsametype);
contents.illusionary_visblocker = func_illusionary_visblocker;
std::optional<bspbrush_t> brush = LoadBrush(src, &mapbrush, contents, hullnum);
if (!brush)
continue;
bspbrush_t brush = LoadBrush(src, &mapbrush, contents, hullnum);
brush->lmshift = lmshift;
brush.lmshift = lmshift;
for (auto &face : brush->sides)
for (auto &face : brush.sides) {
face.lmshift = lmshift;
if (classname == std::string_view("func_areaportal")) {
brush->func_areaportal = const_cast<mapentity_t *>(src); // FIXME: get rid of consts on src in the callers?
}
qbsp_options.target_game->count_contents_in_stats(brush->contents, stats);
dst->brushes.push_back(std::make_unique<bspbrush_t>(brush.value()));
dst->bounds += brush->bounds;
if (classname == std::string_view("func_areaportal")) {
brush.func_areaportal = const_cast<mapentity_t *>(src); // FIXME: get rid of consts on src in the callers?
}
qbsp_options.target_game->count_contents_in_stats(brush.contents, stats);
dst->brushes.push_back(std::make_unique<bspbrush_t>(std::move(brush)));
dst->bounds += brush.bounds;
}
logging::percent(src->mapbrushes.size(), src->mapbrushes.size(), src == map.world_entity());

View File

@ -70,39 +70,6 @@ struct bspstats_t
std::atomic<int> c_brushesonesided;
};
/*
==================
CreateBrushWindings
Currently only used in BrushFromBounds
==================
*/
static void CreateBrushWindings(bspbrush_t *brush)
{
std::optional<winding_t> w;
for (int i = 0; i < brush->sides.size(); i++) {
side_t *side = &brush->sides[i];
w = BaseWindingForPlane(Face_Plane(side));
for (int j = 0; j < brush->sides.size() && w; j++) {
if (i == j)
continue;
if (brush->sides[j].bevel)
continue;
qplane3d plane = -Face_Plane(&brush->sides[j]);
w = w->clip(plane, 0, false)[SIDE_FRONT]; // CLIP_EPSILON);
}
if (w) {
side->w = *w;
} else {
side->w.clear();
}
}
brush->update_bounds();
}
/*
==================
BrushFromBounds
@ -575,6 +542,8 @@ static twosided<std::unique_ptr<bspbrush_t>> SplitBrush(std::unique_ptr<bspbrush
if (!result[0] && !result[1]) {
stats.c_brushesremoved++;
return result;
} else if (!result[0] || !result[1]) {
stats.c_brushesonesided++;

View File

@ -2215,7 +2215,7 @@ void ProcessMapBrushes()
for (auto &entity : map.entities) {
/* Origin brush support */
rotation_t rottype = rotation_t::none;
entity.rotation = rotation_t::none;
for (auto it = entity.mapbrushes.begin(); it != entity.mapbrushes.end(); ) {
auto &brush = *it;
@ -2239,7 +2239,7 @@ void ProcessMapBrushes()
num_removed++;
it = entity.mapbrushes.erase(it);
rottype = rotation_t::origin_brush;
entity.rotation = rotation_t::origin_brush;
continue;
}
@ -2256,17 +2256,16 @@ void ProcessMapBrushes()
map.total_brushes += entity.mapbrushes.size();
/* Hipnotic rotation */
if (rottype == rotation_t::none) {
if (entity.rotation == rotation_t::none) {
if (!Q_strncasecmp(entity.epairs.get("classname"), "rotate_", 7)) {
entity.origin = FixRotateOrigin(&entity);
rottype = rotation_t::hipnotic;
entity.rotation = rotation_t::hipnotic;
}
}
// offset brush bounds
if (rottype != rotation_t::none) {
if (entity.rotation != rotation_t::none) {
for (auto &brush : entity.mapbrushes) {
brush.bounds = brush.bounds.translate(-entity.origin);
for (auto &f : brush.faces) {
// account for texture offset, from txqbsp-xt
@ -2285,35 +2284,12 @@ void ProcessMapBrushes()
f.planenum = map.add_or_find_plane(plane);
}
// re-calculate brush bounds
// re-calculate brush bounds/windings
CalculateBrushBounds(brush);
num_offset++;
}
}
#if 0
// Rotatable objects must have a bounding box big enough to
// account for all its rotations
// if -wrbrushes is in use, don't do this for the clipping hulls because it depends on having
// the actual non-hacked bbox (it doesn't write axial planes).
// Hexen2 also doesn't want the bbox expansion, it's handled in engine (see: SV_LinkEdict)
// Only do this for hipnotic rotation. For origin brushes in Quake, it breaks some of their
// uses (e.g. func_train). This means it's up to the mapper to expand the model bounds with
// clip brushes if they're going to rotate a model in vanilla Quake and not use hipnotic rotation.
// The idea behind the bounds expansion was to avoid incorrect vis culling (AFAIK).
const bool shouldExpand = (rotate_offset[0] != 0.0 || rotate_offset[1] != 0.0 || rotate_offset[2] != 0.0) &&
rottype == rotation_t::hipnotic &&
(hullnum >= 0) // hullnum < 0 corresponds to -wrbrushes clipping hulls
&& qbsp_options.target_game->id != GAME_HEXEN_II; // never do this in Hexen 2
if (shouldExpand) {
vec_t delta = std::max(fabs(max), fabs(min));
hullbrush->bounds = {-delta, delta};
}
#endif
}
logging::print(logging::flag::STAT, " {:8} brushes\n", map.total_brushes);
@ -2715,6 +2691,8 @@ void CalculateWorldExtent(void)
}
qbsp_options.worldextent.setValue((extents + hull_extents) * 2, settings::source::GAME_TARGET);
logging::print("INFO: world extents calculated to {} units\n", qbsp_options.worldextent.value());
}
/*
@ -2774,12 +2752,10 @@ static void TestExpandBrushes(const mapentity_t *src)
std::vector<std::unique_ptr<bspbrush_t>> hull1brushes;
for (auto &mapbrush : src->mapbrushes) {
std::optional<bspbrush_t> hull1brush = LoadBrush(src, &mapbrush, {CONTENTS_SOLID},
bspbrush_t hull1brush = LoadBrush(src, &mapbrush, {CONTENTS_SOLID},
qbsp_options.target_game->id == GAME_QUAKE_II ? HULL_COLLISION : 1);
if (hull1brush) {
hull1brushes.emplace_back(std::make_unique<bspbrush_t>(std::move(*hull1brush)));
}
hull1brushes.emplace_back(std::make_unique<bspbrush_t>(std::move(hull1brush)));
}
WriteBspBrushMap("expanded.map", hull1brushes);

View File

@ -413,7 +413,7 @@ static void MarkVisibleBrushSides_R(node_t *node)
// leaf that are coplanar
for (auto *brush : neighbour_leaf->original_brushes) {
for (auto &side : brush->sides) {
//if (qv::epsilonEqual(side.plane, portal->plane)) {
// fixme-brushbsp: should this be get_plane() ?
if (qv::epsilonEqual(side.get_positive_plane(), portal->plane)) {
// we've found a brush side in an original brush in the neighbouring
// leaf, on a portal to this (non-opaque) leaf, so mark it as visible.

View File

@ -41,6 +41,9 @@ static mapentity_t LoadMap(const char *map)
parser_t parser(map);
// FIXME: ???
mapentity_t &entity = ::map.entities.emplace_back();
mapentity_t worldspawn;
// FIXME: adds the brush to the global map...
@ -399,10 +402,8 @@ TEST_CASE("duplicatePlanes", "[qbsp]")
CHECK(0 == worldspawn.brushes.size());
CHECK(6 == worldspawn.mapbrushes.front().faces.size());
std::optional<bspbrush_t> brush =
LoadBrush(&worldspawn, &worldspawn.mapbrushes.front(), {CONTENTS_SOLID}, 0);
REQUIRE(std::nullopt != brush);
CHECK(6 == brush->sides.size());
bspbrush_t brush = LoadBrush(&worldspawn, &worldspawn.mapbrushes.front(), {CONTENTS_SOLID}, 0);
CHECK(6 == brush.sides.size());
}
/**