Merge branch 'brushbsp' of https://github.com/ericwa/ericw-tools into brushbsp

This commit is contained in:
Jonathan 2022-06-24 04:33:14 -04:00
commit 8b1472dfd1
20 changed files with 195 additions and 157 deletions

View File

@ -53,7 +53,7 @@ constexpr vec_t DIST_EPSILON = 0.0001;
constexpr vec_t DEGREES_EPSILON = 0.001;
constexpr vec_t DEFAULT_ON_EPSILON = 0.1;
enum side_t : int8_t
enum planeside_t : int8_t
{
SIDE_FRONT,
SIDE_BACK,

View File

@ -540,7 +540,7 @@ public:
// dists/sides can be null, or must have (size() + 1) reserved
inline std::array<size_t, SIDE_TOTAL> calc_sides(
const qplane3d &plane, vec_t *dists, side_t *sides, const vec_t &on_epsilon = DEFAULT_ON_EPSILON) const
const qplane3d &plane, vec_t *dists, planeside_t *sides, const vec_t &on_epsilon = DEFAULT_ON_EPSILON) const
{
std::array<size_t, SIDE_TOTAL> counts{};
@ -554,7 +554,7 @@ public:
dists[i] = dot;
}
side_t side;
planeside_t side;
if (dot > on_epsilon)
side = SIDE_FRONT;
@ -593,7 +593,7 @@ public:
const qplane3d &plane, const vec_t &on_epsilon = DEFAULT_ON_EPSILON, const bool &keepon = false) const
{
vec_t *dists = (vec_t *)alloca(sizeof(vec_t) * (count + 1));
side_t *sides = (side_t *)alloca(sizeof(side_t) * (count + 1));
planeside_t *sides = (planeside_t *)alloca(sizeof(planeside_t) * (count + 1));
std::array<size_t, SIDE_TOTAL> counts = calc_sides(plane, dists, sides, on_epsilon);

View File

@ -28,16 +28,33 @@
class mapentity_t;
struct brush_t
struct side_t
{
winding_t w;
int planenum;
planeside_t planeside; // which side is the front of the face
int texinfo;
int16_t lmshift;
qvec3d origin;
vec_t radius;
bool onnode; // has this face been used as a BSP node plane yet?
bool visible = true; // can any part of this side be seen from non-void parts of the level?
// non-visible means we can discard the brush side
// (avoiding generating a BSP spit, so expanding it outwards)
};
struct bspbrush_t {
/**
* The brushes in the mapentity_t::brushes vector are considered originals. Brush fragments created during
* the BrushBSP will have this pointing back to the original brush in mapentity_t::brushes.
*/
brush_t *original;
bspbrush_t *original;
uint32_t file_order;
aabb3d bounds;
std::vector<face_t> faces;
std::vector<side_t> sides;
contentflags_t contents; /* BSP contents */
short lmshift; /* lightmap scaling (qu/lightmap pixel), passed to the light util */
std::optional<uint32_t> outputnumber; /* only set for original brushes */
@ -49,6 +66,7 @@ struct brush_t
class mapbrush_t;
qplane3d Face_Plane(const face_t *face);
qplane3d Face_Plane(const side_t *face);
enum class rotation_t
{
@ -57,10 +75,10 @@ enum class rotation_t
origin_brush
};
std::optional<brush_t> LoadBrush(const mapentity_t *src, const mapbrush_t *mapbrush, const contentflags_t &contents,
std::optional<bspbrush_t> LoadBrush(const mapentity_t *src, const mapbrush_t *mapbrush, const contentflags_t &contents,
const qvec3d &rotate_offset, const rotation_t rottype, const int hullnum);
void FreeBrushes(mapentity_t *ent);
int FindPlane(const qplane3d &plane, side_t *side);
int FindPlane(const qplane3d &plane, planeside_t *side);
int FindPositivePlane(int planenum);
int FindPositivePlane(const qplane3d &plane, side_t *side);
int FindPositivePlane(const qplane3d &plane, planeside_t *side);

View File

@ -31,7 +31,7 @@
extern std::atomic<int> splitnodes;
struct brush_t;
struct bspbrush_t;
struct node_t;
struct face_t;
class mapentity_t;
@ -40,5 +40,5 @@ struct tree_t;
void DetailToSolid(node_t *node);
void PruneNodes(node_t *node);
bool WindingIsTiny(const winding_t &w, double size = 0.2);
twosided<std::unique_ptr<brush_t>> SplitBrush(std::unique_ptr<brush_t> brush, const qplane3d &split);
twosided<std::unique_ptr<bspbrush_t>> SplitBrush(std::unique_ptr<bspbrush_t> brush, const qplane3d &split);
tree_t *BrushBSP(mapentity_t *entity, bool midsplit);

View File

@ -27,13 +27,15 @@
#include <tuple>
#include <vector>
struct brush_t;
struct bspbrush_t;
struct face_t;
struct side_t;
face_t *NewFaceFromFace(const face_t *in);
face_t *CopyFace(const face_t *in);
face_t *MirrorFace(const face_t *face);
std::tuple<face_t *, face_t *> SplitFace(face_t *in, const qplane3d &split);
void UpdateFaceSphere(face_t *in);
bool BrushGE(const brush_t &a, const brush_t &b);
std::vector<std::unique_ptr<brush_t>> ChopBrushes(const std::vector<std::unique_ptr<brush_t>> &input);
void UpdateFaceSphere(side_t *in);
bool BrushGE(const bspbrush_t &a, const bspbrush_t &b);
std::vector<std::unique_ptr<bspbrush_t>> ChopBrushes(const std::vector<std::unique_ptr<bspbrush_t>> &input);

View File

@ -33,7 +33,7 @@
#include <unordered_map>
#include <list>
struct brush_t;
struct bspbrush_t;
struct qbsp_plane_t : qplane3d
{
@ -102,7 +102,7 @@ public:
entdict_t epairs;
aabb3d bounds;
std::vector<std::unique_ptr<brush_t>> brushes;
std::vector<std::unique_ptr<bspbrush_t>> brushes;
int firstoutputfacenumber = -1;
std::optional<size_t> outputmodelnumber = std::nullopt;
@ -218,7 +218,7 @@ constexpr int HULL_COLLISION = -1;
/* Create BSP brushes from map brushes */
void Brush_LoadEntity(mapentity_t *entity, const int hullnum);
std::list<face_t *> CSGFace(face_t *srcface, const mapentity_t* srcentity, const brush_t *srcbrush, const node_t *srcnode);
std::list<face_t *> CSGFace(face_t *srcface, const mapentity_t* srcentity, const bspbrush_t *srcbrush, const node_t *srcnode);
void TJunc(const mapentity_t *entity, node_t *headnode);
int MakeFaceEdges(mapentity_t *entity, node_t *headnode);
void ExportClipNodes(mapentity_t *entity, node_t *headnode, const int hullnum);
@ -232,10 +232,10 @@ void BSPX_Brushes_Finalize(struct bspxbrushes_s *ctx);
void BSPX_Brushes_Init(struct bspxbrushes_s *ctx);
void ExportObj_Faces(const std::string &filesuffix, const std::vector<const face_t *> &faces);
void ExportObj_Brushes(const std::string &filesuffix, const std::vector<const brush_t *> &brushes);
void ExportObj_Brushes(const std::string &filesuffix, const std::vector<const bspbrush_t *> &brushes);
void ExportObj_Nodes(const std::string &filesuffix, const node_t *nodes);
void ExportObj_Marksurfaces(const std::string &filesuffix, const node_t *nodes);
void WriteBspBrushMap(const fs::path &name, const std::vector<std::unique_ptr<brush_t>> &list);
void WriteBspBrushMap(const fs::path &name, const std::vector<std::unique_ptr<bspbrush_t>> &list);
bool IsValidTextureProjection(const qvec3f &faceNormal, const qvec3f &s_vec, const qvec3f &t_vec);

View File

@ -26,6 +26,8 @@
#include <atomic>
struct side_t;
struct portal_t
{
int planenum;
@ -35,7 +37,7 @@ struct portal_t
std::optional<winding_t> winding;
bool sidefound; // false if ->side hasn't been checked
face_t *side; // NULL = non-visible // fixme-brushbsp: change to side_t
side_t *side; // NULL = non-visible
face_t *face[2]; // output face in bsp file
};

View File

@ -332,10 +332,10 @@ struct portal_t;
struct face_t : face_fragment_t
{
int planenum;
side_t planeside; // which side is the front of the face
planeside_t planeside; // which side is the front of the face
int texinfo;
twosided<contentflags_t> contents;
twosided<int16_t> lmshift;
contentflags_t contents; // contents on the front of the face
int16_t lmshift;
qvec3d origin;
vec_t radius;
@ -343,17 +343,12 @@ struct face_t : face_fragment_t
// filled by TJunc
std::vector<face_fragment_t> fragments;
// fixme-brushbsp: move to a brush_side_t struct
bool onnode; // has this face been used as a BSP node plane yet?
bool visible = true; // can any part of this side be seen from non-void parts of the level?
// non-visible means we can discard the brush side
// (avoiding generating a BSP spit, so expanding it outwards)
portal_t *portal;
};
// there is a node_t structure for every node and leaf in the bsp tree
struct brush_t;
struct bspbrush_t;
struct node_t
{
@ -369,7 +364,7 @@ struct node_t
// information for leafs
contentflags_t contents; // leaf nodes (0 for decision nodes)
std::vector<brush_t *> original_brushes;
std::vector<bspbrush_t *> original_brushes;
std::vector<face_t *> markfaces; // leaf nodes only, point to node faces
portal_t *portals;
int visleafnum; // -1 = solid

View File

@ -9,7 +9,7 @@ set(QBSP_INCLUDES
${CMAKE_SOURCE_DIR}/include/qbsp/outside.hh
${CMAKE_SOURCE_DIR}/include/qbsp/portals.hh
${CMAKE_SOURCE_DIR}/include/qbsp/prtfile.hh
${CMAKE_SOURCE_DIR}/include/qbsp/solidbsp.hh
${CMAKE_SOURCE_DIR}/include/qbsp/brushbsp.hh
${CMAKE_SOURCE_DIR}/include/qbsp/surfaces.hh
${CMAKE_SOURCE_DIR}/include/qbsp/writebsp.hh)
@ -22,7 +22,7 @@ set(QBSP_SOURCES
${CMAKE_SOURCE_DIR}/qbsp/portals.cc
${CMAKE_SOURCE_DIR}/qbsp/prtfile.cc
${CMAKE_SOURCE_DIR}/qbsp/qbsp.cc
${CMAKE_SOURCE_DIR}/qbsp/solidbsp.cc
${CMAKE_SOURCE_DIR}/qbsp/brushbsp.cc
${CMAKE_SOURCE_DIR}/qbsp/surfaces.cc
${CMAKE_SOURCE_DIR}/qbsp/tjunc.cc
${CMAKE_SOURCE_DIR}/qbsp/wad.cc

View File

@ -65,6 +65,17 @@ qplane3d Face_Plane(const face_t *face)
return result;
}
qplane3d Face_Plane(const side_t *face)
{
const qplane3d &result = map.planes.at(face->planenum);
if (face->planeside) {
return -result;
}
return result;
}
/*
=================
CheckFace
@ -72,7 +83,7 @@ CheckFace
Note: this will not catch 0 area polygons
=================
*/
static void CheckFace(face_t *face, const mapface_t &sourceface)
static void CheckFace(side_t *face, const mapface_t &sourceface)
{
const qbsp_plane_t &plane = map.planes.at(face->planenum);
@ -206,7 +217,7 @@ static void PlaneHash_Add(const qplane3d &p, int index)
* NewPlane
* - Returns a global plane number and the side that will be the front
*/
static int NewPlane(const qplane3d &plane, side_t *side)
static int NewPlane(const qplane3d &plane, planeside_t *side)
{
vec_t len = qv::length(plane.normal);
@ -235,7 +246,7 @@ static int NewPlane(const qplane3d &plane, side_t *side)
* - Returns a global plane number and the side that will be the front
* - if `side` is null, only an exact match will be fetched.
*/
int FindPlane(const qplane3d &plane, side_t *side)
int FindPlane(const qplane3d &plane, planeside_t *side)
{
for (int i : map.planehash[plane_hash_fn(plane)]) {
const qbsp_plane_t &p = map.planes.at(i);
@ -268,7 +279,7 @@ int FindPositivePlane(int planenum)
return FindPlane(-plane, nullptr);
}
int FindPositivePlane(const qplane3d &plane, side_t *side)
int FindPositivePlane(const qplane3d &plane, planeside_t *side)
{
int planenum = FindPlane(plane, side);
int positive_plane = FindPositivePlane(planenum);
@ -358,13 +369,13 @@ static bool MapBrush_IsHint(const mapbrush_t &brush)
CreateBrushFaces
=================
*/
static std::vector<face_t> CreateBrushFaces(const mapentity_t *src, hullbrush_t *hullbrush, const int hullnum,
static std::vector<side_t> CreateBrushFaces(const mapentity_t *src, hullbrush_t *hullbrush, const int hullnum,
const rotation_t rottype = rotation_t::none, const qvec3d &rotate_offset = {})
{
vec_t r;
std::optional<winding_t> w;
qbsp_plane_t plane;
std::vector<face_t> facelist;
std::vector<side_t> facelist;
qvec3d point;
vec_t max, min;
@ -405,7 +416,7 @@ static std::vector<face_t> CreateBrushFaces(const mapentity_t *src, hullbrush_t
}
// this face is a keeper
face_t &f = facelist.emplace_back();
side_t &f = facelist.emplace_back();
f.planenum = PLANENUM_LEAF;
f.w.resize(w->size());
@ -660,7 +671,7 @@ static void AddHullEdge(hullbrush_t *hullbrush, const qvec3d &p1, const qvec3d &
ExpandBrush
=============
*/
static void ExpandBrush(hullbrush_t *hullbrush, const aabb3d &hull_size, std::vector<face_t> &facelist)
static void ExpandBrush(hullbrush_t *hullbrush, const aabb3d &hull_size, std::vector<side_t> &facelist)
{
int x, s;
qbsp_plane_t plane;
@ -755,11 +766,11 @@ LoadBrush
Converts a mapbrush to a bsp brush
===============
*/
std::optional<brush_t> LoadBrush(const mapentity_t *src, const mapbrush_t *mapbrush, const contentflags_t &contents,
std::optional<bspbrush_t> LoadBrush(const mapentity_t *src, const mapbrush_t *mapbrush, const contentflags_t &contents,
const qvec3d &rotate_offset, const rotation_t rottype, const int hullnum)
{
hullbrush_t hullbrush;
std::vector<face_t> facelist;
std::vector<side_t> facelist;
// create the faces
@ -796,9 +807,9 @@ std::optional<brush_t> LoadBrush(const mapentity_t *src, const mapbrush_t *mapbr
}
// create the brush
brush_t brush{};
bspbrush_t brush{};
brush.contents = contents;
brush.faces = std::move(facelist);
brush.sides = std::move(facelist);
brush.bounds = hullbrush.bounds;
return brush;
}
@ -826,7 +837,7 @@ static void Brush_LoadEntity(mapentity_t *dst, const mapentity_t *src, const int
continue;
}
std::optional<brush_t> brush = LoadBrush(src, mapbrush, contents, {}, rotation_t::none, 0);
std::optional<bspbrush_t> brush = LoadBrush(src, mapbrush, contents, {}, rotation_t::none, 0);
if (brush) {
rotate_offset = brush->bounds.centroid();
@ -946,7 +957,7 @@ static void Brush_LoadEntity(mapentity_t *dst, const mapentity_t *src, const int
*/
if (hullnum != HULL_COLLISION && contents.is_clip(options.target_game)) {
if (hullnum == 0) {
std::optional<brush_t> brush = LoadBrush(src, mapbrush, contents, rotate_offset, rottype, hullnum);
std::optional<bspbrush_t> brush = LoadBrush(src, mapbrush, contents, rotate_offset, rottype, hullnum);
if (brush) {
dst->bounds += brush->bounds;
@ -996,21 +1007,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<brush_t> brush = LoadBrush(src, mapbrush, contents, rotate_offset, rottype, hullnum);
std::optional<bspbrush_t> brush = LoadBrush(src, mapbrush, contents, rotate_offset, rottype, hullnum);
if (!brush)
continue;
brush->lmshift = lmshift;
for (auto &face : brush->faces)
face.lmshift = { (short) lmshift, (short) lmshift };
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?
}
options.target_game->count_contents_in_stats(brush->contents, stats);
dst->brushes.push_back(std::make_unique<brush_t>(brush.value()));
dst->brushes.push_back(std::make_unique<bspbrush_t>(brush.value()));
dst->bounds += brush->bounds;
}
@ -1057,10 +1068,10 @@ void Brush_LoadEntity(mapentity_t *entity, const int hullnum)
options.target_game->print_content_stats(stats, "brushes");
}
void brush_t::update_bounds()
void bspbrush_t::update_bounds()
{
this->bounds = {};
for (const face_t &face : faces) {
for (const auto &face : sides) {
this->bounds = this->bounds.unionWith(face.w.bounds());
}
}

View File

@ -19,7 +19,7 @@
See file, 'COPYING', for details.
*/
#include <qbsp/solidbsp.hh>
#include <qbsp/brushbsp.hh>
#include <climits>
@ -142,7 +142,7 @@ FaceSide
For BSP hueristic
==================
*/
static int FaceSide__(const face_t *in, const qbsp_plane_t &split)
static int FaceSide__(const winding_t &w, const qbsp_plane_t &split)
{
bool have_front, have_back;
int i;
@ -151,8 +151,8 @@ static int FaceSide__(const face_t *in, const qbsp_plane_t &split)
if (split.type < plane_type_t::PLANE_ANYX) {
/* shortcut for axial planes */
const vec_t *p = &in->w[0][static_cast<size_t>(split.type)];
for (i = 0; i < in->w.size(); i++, p += 3) {
const vec_t *p = &w[0][static_cast<size_t>(split.type)];
for (i = 0; i < w.size(); i++, p += 3) {
if (*p > split.dist + options.epsilon.value()) {
if (have_back)
return SIDE_ON;
@ -165,8 +165,8 @@ static int FaceSide__(const face_t *in, const qbsp_plane_t &split)
}
} else {
/* sloping planes take longer */
for (i = 0; i < in->w.size(); i++) {
const vec_t dot = split.distance_to(in->w[i]);
for (i = 0; i < w.size(); i++) {
const vec_t dot = split.distance_to(w[i]);
if (dot > options.epsilon.value()) {
if (have_back)
return SIDE_ON;
@ -196,7 +196,19 @@ inline int FaceSide(const face_t *in, const qbsp_plane_t &split)
else if (dist < -in->radius)
return SIDE_BACK;
else
return FaceSide__(in, split);
return FaceSide__(in->w, split);
}
inline int FaceSide(const side_t *in, const qbsp_plane_t &split)
{
vec_t dist = split.distance_to(in->origin);
if (dist > in->radius)
return SIDE_FRONT;
else if (dist < -in->radius)
return SIDE_BACK;
else
return FaceSide__(in->w, split);
}
/*
@ -309,13 +321,13 @@ ChooseMidPlaneFromList
The clipping hull BSP doesn't worry about avoiding splits
==================
*/
static const face_t *ChooseMidPlaneFromList(const std::vector<std::unique_ptr<brush_t>> &brushes, const aabb3d &bounds)
static const side_t *ChooseMidPlaneFromList(const std::vector<std::unique_ptr<bspbrush_t>> &brushes, const aabb3d &bounds)
{
/* pick the plane that splits the least */
vec_t bestaxialmetric = VECT_MAX;
face_t *bestaxialsurface = nullptr;
side_t *bestaxialsurface = nullptr;
vec_t bestanymetric = VECT_MAX;
face_t *bestanysurface = nullptr;
side_t *bestanysurface = nullptr;
for (int pass = 0; pass < 2; pass++) {
for (auto &brush : brushes) {
@ -323,7 +335,7 @@ static const face_t *ChooseMidPlaneFromList(const std::vector<std::unique_ptr<br
continue;
}
for (auto &face : brush->faces) {
for (auto &face : brush->sides) {
if (face.onnode)
continue;
if (!face.visible) {
@ -375,12 +387,12 @@ The real BSP heuristic
fixme-brushbsp: prefer splits that include a lot of brush sides?
==================
*/
static const face_t *ChoosePlaneFromList(const std::vector<std::unique_ptr<brush_t>> &brushes, const aabb3d &bounds)
static const side_t *ChoosePlaneFromList(const std::vector<std::unique_ptr<bspbrush_t>> &brushes, const aabb3d &bounds)
{
/* pick the plane that splits the least */
int minsplits = INT_MAX - 1;
vec_t bestdistribution = VECT_MAX;
face_t *bestsurface = nullptr;
side_t *bestsurface = nullptr;
/* passes:
* 0: structural visible
@ -397,7 +409,7 @@ static const face_t *ChoosePlaneFromList(const std::vector<std::unique_ptr<brush
continue;
}
for (auto &face : brush->faces) {
for (auto &face : brush->sides) {
if (face.onnode) {
continue;
}
@ -413,7 +425,7 @@ static const face_t *ChoosePlaneFromList(const std::vector<std::unique_ptr<brush
// now check all of the other faces in `brushes` and count how many
// would get split if we used `face` as the splitting plane
for (auto &brush2 : brushes) {
for (auto &face2 : brush2->faces) {
for (auto &face2 : brush2->sides) {
if (face2.planenum == face.planenum || face2.onnode)
continue;
if (!face2.visible)
@ -477,7 +489,7 @@ returns NULL if the surface list can not be divided any more (a leaf)
Called in parallel.
==================
*/
static const face_t *SelectPartition(const std::vector<std::unique_ptr<brush_t>> &brushes)
static const side_t *SelectPartition(const std::vector<std::unique_ptr<bspbrush_t>> &brushes)
{
// calculate a bounding box of the entire surfaceset
aabb3d bounds;
@ -569,11 +581,11 @@ BrushMostlyOnSide
==================
*/
side_t BrushMostlyOnSide(const brush_t &brush, const qplane3d &plane)
planeside_t BrushMostlyOnSide(const bspbrush_t &brush, const qplane3d &plane)
{
vec_t max = 0;
side_t side = SIDE_FRONT;
for (auto &face : brush.faces) {
planeside_t side = SIDE_FRONT;
for (auto &face : brush.sides) {
for (size_t j = 0; j < face.w.size(); j++) {
vec_t d = qv::dot(face.w[j], plane.normal) - plane.dist;
if (d > max) {
@ -595,13 +607,13 @@ BrushVolume
==================
*/
vec_t BrushVolume(const brush_t &brush)
vec_t BrushVolume(const bspbrush_t &brush)
{
// grab the first valid point as the corner
bool found = false;
qvec3d corner;
for (auto &face : brush.faces) {
for (auto &face : brush.sides) {
if (face.w.size() > 0) {
corner = face.w[0];
found = true;
@ -614,7 +626,7 @@ vec_t BrushVolume(const brush_t &brush)
// make tetrahedrons to all other faces
vec_t volume = 0;
for (auto &face : brush.faces) {
for (auto &face : brush.sides) {
auto plane = Face_Plane(&face);
vec_t d = -(qv::dot(corner, plane.normal) - plane.dist);
vec_t area = face.w.area();
@ -635,14 +647,14 @@ input.
https://github.com/id-Software/Quake-2-Tools/blob/master/bsp/qbsp3/brushbsp.c#L935
================
*/
twosided<std::unique_ptr<brush_t>> SplitBrush(std::unique_ptr<brush_t> brush, const qplane3d &split)
twosided<std::unique_ptr<bspbrush_t>> SplitBrush(std::unique_ptr<bspbrush_t> brush, const qplane3d &split)
{
twosided<std::unique_ptr<brush_t>> result;
twosided<std::unique_ptr<bspbrush_t>> result;
// check all points
vec_t d_front = 0;
vec_t d_back = 0;
for (auto &face : brush->faces) {
for (auto &face : brush->sides) {
for (int j = 0; j < face.w.size(); j++) {
vec_t d = qv::dot(face.w[j], split.normal) - split.dist;
if (d > 0 && d > d_front)
@ -664,7 +676,7 @@ twosided<std::unique_ptr<brush_t>> SplitBrush(std::unique_ptr<brush_t> brush, co
// create a new winding from the split plane
auto w = std::optional<winding_t>{BaseWindingForPlane(split)};
for (auto &face : brush->faces) {
for (auto &face : brush->sides) {
if (!w) {
break;
}
@ -673,7 +685,7 @@ twosided<std::unique_ptr<brush_t>> SplitBrush(std::unique_ptr<brush_t> brush, co
}
if (!w || WindingIsTiny(*w)) { // the brush isn't really split
side_t side = BrushMostlyOnSide(*brush, split);
planeside_t side = BrushMostlyOnSide(*brush, split);
if (side == SIDE_FRONT)
result.front = std::move(brush);
else
@ -692,9 +704,9 @@ twosided<std::unique_ptr<brush_t>> SplitBrush(std::unique_ptr<brush_t> brush, co
// start with 2 empty brushes
for (int i = 0; i < 2; i++) {
result[i] = std::make_unique<brush_t>();
result[i] = std::make_unique<bspbrush_t>();
result[i]->original = brush->original;
// fixme-brushbsp: add a brush_t copy constructor to make sure we get all fields
// fixme-brushbsp: add a bspbrush_t copy constructor to make sure we get all fields
result[i]->contents = brush->contents;
result[i]->lmshift = brush->lmshift;
result[i]->func_areaportal = brush->func_areaportal;
@ -702,7 +714,7 @@ twosided<std::unique_ptr<brush_t>> SplitBrush(std::unique_ptr<brush_t> brush, co
// split all the current windings
for (const auto &face : brush->faces) {
for (const auto &face : brush->sides) {
auto cw = face.w.clip(split, 0 /*PLANESIDE_EPSILON*/);
for (size_t j = 0; j < 2; j++) {
if (!cw[j])
@ -716,13 +728,13 @@ twosided<std::unique_ptr<brush_t>> SplitBrush(std::unique_ptr<brush_t> brush, co
#endif
// add the clipped face to result[j]
face_t faceCopy = face;
side_t faceCopy = face;
faceCopy.w = *cw[j];
// fixme-brushbsp: configure any settings on the faceCopy?
// Q2 does `cs->tested = false;`, why?
result[j]->faces.push_back(std::move(faceCopy));
result[j]->sides.push_back(std::move(faceCopy));
}
}
@ -740,7 +752,7 @@ twosided<std::unique_ptr<brush_t>> SplitBrush(std::unique_ptr<brush_t> brush, co
}
}
if (result[i]->faces.size() < 3 || bogus) {
if (result[i]->sides.size() < 3 || bogus) {
result[i] = nullptr;
}
}
@ -762,7 +774,7 @@ twosided<std::unique_ptr<brush_t>> SplitBrush(std::unique_ptr<brush_t> brush, co
// add the midwinding to both sides
for (int i = 0; i < 2; i++) {
face_t cs{};
side_t cs{};
const bool brushOnFront = (i == 0);
@ -775,7 +787,7 @@ twosided<std::unique_ptr<brush_t>> SplitBrush(std::unique_ptr<brush_t> brush, co
cs.w = brushOnFront ? midwinding.flip() : midwinding;
result[i]->faces.push_back(std::move(cs));
result[i]->sides.push_back(std::move(cs));
}
{
@ -806,7 +818,7 @@ inline void DivideNodeBounds(node_t *node, const qbsp_plane_t &split)
DivideBounds(node->bounds, split, node->children[0]->bounds, node->children[1]->bounds);
}
static bool AllDetail(const std::vector<std::unique_ptr<brush_t>> &brushes)
static bool AllDetail(const std::vector<std::unique_ptr<bspbrush_t>> &brushes)
{
for (auto &brush : brushes) {
if (!brush->contents.is_any_detail(options.target_game)) {
@ -826,7 +838,7 @@ original faces that have some fragment inside this leaf.
Called in parallel.
==================
*/
static void CreateLeaf(std::vector<std::unique_ptr<brush_t>> brushes, node_t *leafnode)
static void CreateLeaf(std::vector<std::unique_ptr<bspbrush_t>> brushes, node_t *leafnode)
{
leafnode->facelist.clear();
leafnode->planenum = PLANENUM_LEAF;
@ -850,9 +862,9 @@ PartitionBrushes
Called in parallel.
==================
*/
static void PartitionBrushes(std::vector<std::unique_ptr<brush_t>> brushes, node_t *node)
static void PartitionBrushes(std::vector<std::unique_ptr<bspbrush_t>> brushes, node_t *node)
{
face_t *split = const_cast<face_t *>(SelectPartition(brushes));
auto *split = const_cast<side_t *>(SelectPartition(brushes));
if (split == nullptr) { // this is a leaf node
node->planenum = PLANENUM_LEAF;
@ -876,7 +888,7 @@ static void PartitionBrushes(std::vector<std::unique_ptr<brush_t>> brushes, node
DivideNodeBounds(node, splitplane);
// multiple surfaces, so split all the polysurfaces into front and back lists
std::vector<std::unique_ptr<brush_t>> frontlist, backlist;
std::vector<std::unique_ptr<bspbrush_t>> frontlist, backlist;
for (auto &brush : brushes) {
// NOTE: we're destroying `brushes` here with the std::move()
@ -885,7 +897,7 @@ static void PartitionBrushes(std::vector<std::unique_ptr<brush_t>> brushes, node
// mark faces which were used as a splitter
for (auto &brushMaybe : frags) {
if (brushMaybe) {
for (auto &face : brushMaybe->faces) {
for (auto &face : brushMaybe->sides) {
if (face.planenum == split->planenum) {
face.onnode = true;
}
@ -894,13 +906,13 @@ static void PartitionBrushes(std::vector<std::unique_ptr<brush_t>> brushes, node
}
if (frags.front) {
if (frags.front->faces.empty()) {
if (frags.front->sides.empty()) {
FError("Surface with no faces");
}
frontlist.emplace_back(std::move(frags.front));
}
if (frags.back) {
if (frags.back->faces.empty()) {
if (frags.back->sides.empty()) {
FError("Surface with no faces");
}
backlist.emplace_back(std::move(frags.back));
@ -952,7 +964,7 @@ tree_t *BrushBSP(mapentity_t *entity, bool midsplit)
int visible_brush_sides = 0;
int invisible_brush_sides = 0;
for (const auto &brush : entity->brushes) {
for (auto &side : brush->faces) {
for (auto &side : brush->sides) {
if (side.visible) {
++visible_brush_sides;
} else {
@ -977,9 +989,9 @@ tree_t *BrushBSP(mapentity_t *entity, bool midsplit)
mapbrushes = entity->brushes.size();
// set the original pointers
std::vector<std::unique_ptr<brush_t>> brushcopies;
std::vector<std::unique_ptr<bspbrush_t>> brushcopies;
for (const auto &original : entity->brushes) {
auto copy = std::make_unique<brush_t>(*original);
auto copy = std::make_unique<bspbrush_t>(*original);
copy->original = original.get();
brushcopies.push_back(std::move(copy));
}

View File

@ -23,7 +23,7 @@
#include <qbsp/brush.hh>
#include <qbsp/csg4.hh>
#include <qbsp/map.hh>
#include <qbsp/solidbsp.hh>
#include <qbsp/brushbsp.hh>
#include <qbsp/qbsp.hh>
#include <atomic>
@ -80,6 +80,16 @@ void UpdateFaceSphere(face_t *in)
in->radius = sqrt(in->radius);
}
void UpdateFaceSphere(side_t *in)
{
in->origin = in->w.center();
in->radius = 0;
for (size_t i = 0; i < in->w.size(); i++) {
in->radius = max(in->radius, qv::distance2(in->w[i], in->origin));
}
in->radius = sqrt(in->radius);
}
/*
==================
SplitFace
@ -131,9 +141,7 @@ face_t *MirrorFace(const face_t *face)
{
face_t *newface = NewFaceFromFace(face);
newface->w = face->w.flip();
newface->planeside = static_cast<side_t>(face->planeside ^ 1);
newface->contents.swap();
newface->lmshift.swap();
newface->planeside = static_cast<planeside_t>(face->planeside ^ 1);
return newface;
}

View File

@ -73,7 +73,7 @@ static void ExportObjFace(std::ofstream &f, const face_t *face, int *vertcount)
}
// fixme-brushbsp
fmt::print(f, "usemtl contents{}\n", face->contents[0].native);
fmt::print(f, "usemtl contents{}\n", face->contents.native);
f << 'f';
for (int i = 0; i < face->w.size(); i++) {
// .obj vertexes start from 1
@ -122,15 +122,8 @@ void ExportObj_Faces(const std::string &filesuffix, const std::vector<const face
}
}
void ExportObj_Brushes(const std::string &filesuffix, const std::vector<const brush_t *> &brushes)
void ExportObj_Brushes(const std::string &filesuffix, const std::vector<const bspbrush_t *> &brushes)
{
std::vector<const face_t *> faces;
for (const brush_t *brush : brushes)
for (auto &face : brush->faces)
faces.push_back(&face);
ExportObj_Faces(filesuffix, faces);
}
static void ExportObj_Nodes_r(const node_t *node, std::vector<const face_t *> *dest)

View File

@ -2284,7 +2284,7 @@ WriteBspBrushMap
from q3map
==================
*/
void WriteBspBrushMap(const fs::path &name, const std::vector<std::unique_ptr<brush_t>> &list)
void WriteBspBrushMap(const fs::path &name, const std::vector<std::unique_ptr<bspbrush_t>> &list)
{
logging::print("writing {}\n", name);
std::ofstream f(name);
@ -2296,7 +2296,7 @@ void WriteBspBrushMap(const fs::path &name, const std::vector<std::unique_ptr<br
for (auto &brush : list) {
fmt::print(f, "{{\n");
for (auto &face : brush->faces) {
for (auto &face : brush->sides) {
// FIXME: Factor out this mess
const qbsp_plane_t &plane = map.planes.at(face.planenum);
winding_t w = BaseWindingForPlane(face.planeside ? -plane : plane);
@ -2330,16 +2330,16 @@ from q3map
*/
static void TestExpandBrushes(const mapentity_t *src)
{
std::vector<std::unique_ptr<brush_t>> hull1brushes;
std::vector<std::unique_ptr<bspbrush_t>> hull1brushes;
for (int i = 0; i < src->nummapbrushes; i++) {
const mapbrush_t *mapbrush = &src->mapbrush(i);
std::optional<brush_t> hull1brush = LoadBrush(
std::optional<bspbrush_t> hull1brush = LoadBrush(
src, mapbrush, {CONTENTS_SOLID}, {}, rotation_t::none, options.target_game->id == GAME_QUAKE_II ? HULL_COLLISION : 1);
if (hull1brush) {
hull1brushes.emplace_back(
std::make_unique<brush_t>(std::move(*hull1brush)));
std::make_unique<bspbrush_t>(std::move(*hull1brush)));
}
}

View File

@ -70,7 +70,7 @@ static face_t *TryMerge(face_t *f1, face_t *f2)
if (!f1->w.size() || !f2->w.size() || f1->planeside != f2->planeside || f1->texinfo != f2->texinfo ||
/*!f1->contents[0].equals(options.target_game, f2->contents[0]) || !f1->contents[1].equals(options.target_game, f2->contents[1]) || */
f1->lmshift[0] != f2->lmshift[0] || f1->lmshift[1] != f2->lmshift[1])
f1->lmshift != f2->lmshift)
return NULL;
// find a common edge

View File

@ -352,7 +352,7 @@ std::vector<node_t *> FindOccupiedClusters(node_t *headnode)
static void MarkBrushSidesInvisible(mapentity_t *entity)
{
for (auto &brush : entity->brushes) {
for (auto &face : brush->faces) {
for (auto &face : brush->sides) {
face.visible = false;
}
}
@ -368,7 +368,7 @@ static void MarkAllBrushSidesVisible_R(node_t *node)
}
for (auto *brush : node->original_brushes) {
for (auto &side : brush->faces) {
for (auto &side : brush->sides) {
side.visible = true;
}
}
@ -410,7 +410,7 @@ static void MarkVisibleBrushSides_R(node_t *node)
// optimized case: just mark the brush sides in the neighbouring
// leaf that are coplanar
for (auto *brush : neighbour_leaf->original_brushes) {
for (auto &side : brush->faces) {
for (auto &side : brush->sides) {
if (side.planenum == portal->planenum) {
// 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

@ -24,7 +24,7 @@
#include <qbsp/portals.hh>
#include <qbsp/map.hh>
#include <qbsp/solidbsp.hh>
#include <qbsp/brushbsp.hh>
#include <qbsp/qbsp.hh>
#include <atomic>
@ -98,7 +98,7 @@ void MakeHeadnodePortals(tree_t *tree)
int i, j, n;
portal_t *p, *portals[6];
qbsp_plane_t bplanes[6];
side_t side;
planeside_t side;
// pad with some space so there will never be null volume leafs
aabb3d bounds = tree->bounds.grow(SIDESPACE);
@ -167,7 +167,7 @@ std::optional<winding_t> BaseWindingForNode(node_t *node)
{
plane = map.planes.at(np->planenum);
const side_t keep = (np->children[0] == node) ?
const planeside_t keep = (np->children[0] == node) ?
SIDE_FRONT : SIDE_BACK;
w = w->clip(plane, BASE_WINDING_EPSILON, false)[keep];
@ -247,7 +247,7 @@ void SplitNodePortals(node_t *node, portalstats_t &stats)
portal_t *next_portal = nullptr;
for (portal_t *p = node->portals; p ; p = next_portal)
{
side_t side;
planeside_t side;
if (p->nodes[SIDE_FRONT] == node)
side = SIDE_FRONT;
else if (p->nodes[SIDE_BACK] == node)
@ -445,7 +445,7 @@ static void FindPortalSide(portal_t *p)
return;
int planenum = p->onnode->planenum;
face_t *bestside = nullptr;
side_t *bestside = nullptr;
float bestdot = 0;
for (int j = 0; j < 2; j++)
@ -460,7 +460,7 @@ static void FindPortalSide(portal_t *p)
auto *brush = *it;
if (!options.target_game->contents_contains(brush->contents, viscontents))
continue;
for (face_t &side : brush->faces)
for (auto &side : brush->sides)
{
// fixme-brushbsp: port these
// if (side.bevel)
@ -537,7 +537,7 @@ void MarkVisibleSides(tree_t *tree, mapentity_t* entity)
// clear all the visible flags
for (auto &brush : entity->brushes) {
for (auto &face : brush->faces) {
for (auto &face : brush->sides) {
face.visible = false;
}
}

View File

@ -35,7 +35,7 @@
#include <qbsp/merge.hh>
#include <qbsp/portals.hh>
#include <qbsp/prtfile.hh>
#include <qbsp/solidbsp.hh>
#include <qbsp/brushbsp.hh>
#include <qbsp/surfaces.hh>
#include <qbsp/qbsp.hh>
#include <qbsp/wad.hh>
@ -296,12 +296,12 @@ Adds any additional planes necessary to allow the brush to be expanded
against axial bounding boxes
=================
*/
static std::vector<std::tuple<size_t, const face_t *>> AddBrushBevels(const brush_t &b)
static std::vector<std::tuple<size_t, const side_t *>> AddBrushBevels(const bspbrush_t &b)
{
// add already-present planes
std::vector<std::tuple<size_t, const face_t *>> planes;
std::vector<std::tuple<size_t, const side_t *>> planes;
for (auto &f : b.faces) {
for (auto &f : b.sides) {
int32_t planenum = f.planenum;
if (f.planeside) {
@ -336,7 +336,7 @@ static std::vector<std::tuple<size_t, const face_t *>> AddBrushBevels(const brus
int32_t planenum = FindPlane(new_plane, nullptr);
int32_t outputplanenum = ExportMapPlane(planenum);
planes.emplace_back(outputplanenum, &b.faces.front());
planes.emplace_back(outputplanenum, &b.sides.front());
}
// if the plane is not in it canonical order, swap it
@ -384,11 +384,11 @@ static std::vector<std::tuple<size_t, const face_t *>> AddBrushBevels(const brus
continue;
current.dist = qv::dot(w[j], current.normal);
auto it = b.faces.begin();
auto it = b.sides.begin();
// if all the points on all the sides are
// behind this plane, it is a proper edge bevel
for (; it != b.faces.end(); it++) {
for (; it != b.sides.end(); it++) {
auto &f = *it;
const auto &plane = map.planes.at(f.planenum);
qplane3d temp = f.planeside ? -plane : plane;
@ -410,13 +410,13 @@ static std::vector<std::tuple<size_t, const face_t *>> AddBrushBevels(const brus
break;
}
if (it != b.faces.end())
if (it != b.sides.end())
continue; // wasn't part of the outer hull
// add this plane
int32_t planenum = FindPlane(current, nullptr);
int32_t outputplanenum = ExportMapPlane(planenum);
planes.emplace_back(outputplanenum, &b.faces.front());
planes.emplace_back(outputplanenum, &b.sides.front());
}
}
}
@ -1019,13 +1019,13 @@ hull sizes
*/
static void BSPX_Brushes_AddModel(
struct bspxbrushes_s *ctx, int modelnum, std::vector<std::unique_ptr<brush_t>> &brushes)
struct bspxbrushes_s *ctx, int modelnum, std::vector<std::unique_ptr<bspbrush_t>> &brushes)
{
bspxbrushes_permodel permodel{1, modelnum};
for (auto &b : brushes) {
permodel.numbrushes++;
for (auto &f : b->faces) {
for (auto &f : b->sides) {
/*skip axial*/
const auto &plane = map.planes.at(f.planenum);
if (fabs(plane.normal[0]) == 1 || fabs(plane.normal[1]) == 1 ||
@ -1044,7 +1044,7 @@ static void BSPX_Brushes_AddModel(
for (auto &b : brushes) {
bspxbrushes_perbrush perbrush{};
for (auto &f : b->faces) {
for (auto &f : b->sides) {
/*skip axial*/
const auto &plane = map.planes.at(f.planenum);
if (fabs(plane.normal[0]) == 1 || fabs(plane.normal[1]) == 1 ||
@ -1086,7 +1086,7 @@ static void BSPX_Brushes_AddModel(
str <= perbrush;
for (auto &f : b->faces) {
for (auto &f : b->sides) {
/*skip axial*/
const auto &plane = map.planes.at(f.planenum);
if (fabs(plane.normal[0]) == 1 || fabs(plane.normal[1]) == 1 ||

View File

@ -24,7 +24,7 @@
#include <qbsp/csg4.hh>
#include <qbsp/map.hh>
#include <qbsp/merge.hh>
#include <qbsp/solidbsp.hh>
#include <qbsp/brushbsp.hh>
#include <qbsp/qbsp.hh>
#include <qbsp/writebsp.hh>
@ -39,7 +39,7 @@ static bool ShouldOmitFace(face_t *f)
return true;
// HACK: to save a few faces, don't output the interior faces of sky brushes
if (f->contents[0].is_sky(options.target_game)) {
if (f->contents.is_sky(options.target_game)) {
return true;
}
@ -81,7 +81,7 @@ std::list<face_t *> SubdivideFace(face_t *f)
// one lightmap block will always be added at the end, for smooth interpolation
// engines that do support scaling will support 256*256 blocks (at whatever scale).
lmshift = f->lmshift[0];
lmshift = f->lmshift;
if (lmshift > 4)
lmshift = 4; // no bugging out with legacy lighting
@ -292,7 +292,7 @@ Returns a global edge number, possibly negative to indicate a backwards edge.
*/
inline size_t GetEdge(mapentity_t *entity, const qvec3d &p1, const qvec3d &p2, const face_t *face)
{
if (!face->contents[0].is_valid(options.target_game, false))
if (!face->contents.is_valid(options.target_game, false))
FError("Face with invalid contents");
size_t v1 = GetVertex(p1);
@ -304,7 +304,7 @@ inline size_t GetEdge(mapentity_t *entity, const qvec3d &p1, const qvec3d &p2, c
auto it = hashedges.find(edge_hash_key);
if (it != hashedges.end()) {
for (const int i : it->second) {
if (pEdgeFaces1[i] == NULL && pEdgeFaces0[i]->contents[0].native == face->contents[0].native) {
if (pEdgeFaces1[i] == NULL && pEdgeFaces0[i]->contents.native == face->contents.native) {
pEdgeFaces1[i] = face;
return -i;
}
@ -392,7 +392,7 @@ static void EmitFaceFragment(mapentity_t *entity, face_t *face, face_fragment_t
mface_t &out = map.bsp.dfaces.emplace_back();
// emit lmshift
map.exported_lmshifts.push_back(face->lmshift[1]);
map.exported_lmshifts.push_back(face->lmshift);
Q_assert(map.bsp.dfaces.size() == map.exported_lmshifts.size());
out.planenum = ExportMapPlane(face->planenum);
@ -458,7 +458,7 @@ static void CountFace(mapentity_t *entity, face_t *f, size_t &facesCount, size_t
if (ShouldOmitFace(f))
return;
if (f->lmshift[1] != 4)
if (f->lmshift != 4)
map.needslmshifts = true;
facesCount++;
@ -597,7 +597,7 @@ see also FindPortalSide which populates p->side
*/
static face_t *FaceFromPortal(portal_t *p, int pside)
{
face_t *side = p->side;
side_t *side = p->side;
if (!side)
return nullptr; // portal does not bridge different visible contents
@ -605,7 +605,7 @@ static face_t *FaceFromPortal(portal_t *p, int pside)
f->texinfo = side->texinfo;
f->planenum = side->planenum;
f->planeside = static_cast<side_t>(pside);
f->planeside = static_cast<planeside_t>(pside);
f->portal = p;
f->lmshift = side->lmshift;
@ -632,15 +632,12 @@ static face_t *FaceFromPortal(portal_t *p, int pside)
if (pside)
{
f->w = p->winding->flip();
// fixme-brushbsp: was just `f->contents` on qbsp3
f->contents[0] = p->nodes[1]->contents;
f->contents[1] = p->nodes[0]->contents;
f->contents = p->nodes[1]->contents;
}
else
{
f->w = *p->winding;
f->contents[0] = p->nodes[0]->contents;
f->contents[1] = p->nodes[1]->contents;
f->contents = p->nodes[0]->contents;
}
UpdateFaceSphere(f);

View File

@ -351,10 +351,10 @@ TEST_CASE("duplicatePlanes", "[qbsp]")
CHECK(0 == worldspawn.brushes.size());
CHECK(6 == worldspawn.mapbrush(0).numfaces);
std::optional<brush_t> brush =
std::optional<bspbrush_t> brush =
LoadBrush(&worldspawn, &worldspawn.mapbrush(0), {CONTENTS_SOLID}, {}, rotation_t::none, 0);
REQUIRE(std::nullopt != brush);
CHECK(6 == brush->faces.size());
CHECK(6 == brush->sides.size());
}
/**