diff --git a/bspinfo/bspinfo.cc b/bspinfo/bspinfo.cc index 21427535..330dba45 100644 --- a/bspinfo/bspinfo.cc +++ b/bspinfo/bspinfo.cc @@ -109,7 +109,22 @@ static void serialize_bsp(const bspdata_t &bspdata, const mbsp_t &bsp, const std } if (bsp.dvis.bits.size()) { - j["visdata"] = hex_string(bsp.dvis.bits.data(), bsp.dvis.bits.size()); + + if (bsp.dvis.bit_offsets.size()) { + json &visdata = (j.emplace("visdata", json::object())).first.value(); + + json &pvs = (visdata.emplace("pvs", json::array())).first.value(); + json &phs = (visdata.emplace("pvs", json::array())).first.value(); + + for (auto &offset : bsp.dvis.bit_offsets) { + pvs.push_back(offset[VIS_PVS]); + phs.push_back(offset[VIS_PHS]); + } + + visdata["bits"] = hex_string(bsp.dvis.bits.data(), bsp.dvis.bits.size()); + } else { + j["visdata"] = hex_string(bsp.dvis.bits.data(), bsp.dvis.bits.size()); + } } if (bsp.dlightdata.size()) { @@ -117,7 +132,7 @@ static void serialize_bsp(const bspdata_t &bspdata, const mbsp_t &bsp, const std } if (!bsp.dentdata.empty()) { - j["entdata"] = bsp.dentdata + '\0'; + j["entdata"] = bsp.dentdata; } if (!bsp.dleafs.empty()) { @@ -324,10 +339,11 @@ int main(int argc, char **argv) LoadBSPFile(source, &bsp); PrintBSPFileSizes(&bsp); - source.replace_extension("bsp.json"); + WriteBSPFile(std::filesystem::path(source).replace_extension("bsp.rewrite"), &bsp); + ConvertBSPFormat(&bsp, &bspver_generic); - serialize_bsp(bsp, std::get(bsp.bsp), source); + serialize_bsp(bsp, std::get(bsp.bsp), std::filesystem::path(source).replace_extension("bsp.json")); printf("---------------------\n"); } diff --git a/bsputil/bsputil.cc b/bsputil/bsputil.cc index 9b378ec2..fef43c05 100644 --- a/bsputil/bsputil.cc +++ b/bsputil/bsputil.cc @@ -124,8 +124,7 @@ static void CheckBSPFacesPlanar(const mbsp_t *bsp) dplane_t plane = bsp->dplanes[face->planenum]; if (face->side) { - VectorInverse(plane.normal); - plane.dist = -plane.dist; + plane = -plane; } for (size_t j = 0; j < face->numedges; j++) { diff --git a/bsputil/decompile.cpp b/bsputil/decompile.cpp index ad8fca48..69118dba 100644 --- a/bsputil/decompile.cpp +++ b/bsputil/decompile.cpp @@ -108,20 +108,15 @@ static void WriteNullTexdef(fmt::memory_buffer &file) fmt::format_to(file, "[ {} {} {} {} ] [ {} {} {} {} ] {} {} {}", 1, 0, 0, 0, 0, 1, 0, 0, 0.0, 1, 1); } -// - -struct decomp_plane_t +// this should be an outward-facing plane +struct decomp_plane_t : qplane3d { const bsp2_dnode_t *node; // can be nullptr bool nodefront; // only set if node is non-null. true = we are visiting the front side of the plane - // this should be an outward-facing plane - qvec3d normal; - double distance; - static decomp_plane_t make(const qvec3d &normalIn, double distanceIn) { - return {nullptr, false, normalIn, distanceIn}; + return {{ normalIn, distanceIn }, nullptr, false}; } }; @@ -142,7 +137,7 @@ std::vector RemoveRedundantPlanes(const std::vector winding = winding_t::from_plane(plane.normal, plane.distance, 10e6); + std::optional winding = winding_t::from_plane(plane, 10e6); // clip `winding` by all of the other planes, flipped for (const decomp_plane_t &plane2 : planes) { @@ -150,12 +145,8 @@ std::vector RemoveRedundantPlanes(const std::vectorclip(plane2normal, plane2dist); + auto clipped = winding->clip(-plane2); // discard the back, continue clipping the front part winding = clipped[0]; @@ -210,13 +201,13 @@ std::tuple MakeTangentAndBitangentUnnormalized(const qvec3d &nor return {tangent, bitangent}; } -static planepoints NormalDistanceToThreePoints(const qvec3d &normal, const double dist) +static planepoints NormalDistanceToThreePoints(const qplane3d &plane) { - std::tuple tanBitan = MakeTangentAndBitangentUnnormalized(normal); + std::tuple tanBitan = MakeTangentAndBitangentUnnormalized(plane.normal); planepoints result; - result.point0 = normal * dist; + result.point0 = plane.normal * plane.dist; result.point1 = result.point0 + std::get<1>(tanBitan); result.point2 = result.point0 + std::get<0>(tanBitan); @@ -231,7 +222,7 @@ void PrintPoint(const qvec3d &v, fmt::memory_buffer &file) static void PrintPlanePoints(const mbsp_t *bsp, const decomp_plane_t &decompplane, fmt::memory_buffer &file) { // we have a plane in (normal, distance) form; - const planepoints p = NormalDistanceToThreePoints(decompplane.normal, decompplane.distance); + const planepoints p = NormalDistanceToThreePoints(decompplane); PrintPoint(p.point0, file); fmt::format_to(file, " "); @@ -295,9 +286,9 @@ public: /** * Returns the { front, back } after the clip. */ - std::pair clipToPlane(const qvec3d &normal, double distance) const + std::pair clipToPlane(const qplane3d &plane) const { - auto clipped = winding->clip(normal, (float)distance); + auto clipped = winding->clip(plane); // front or back may be null (if fully clipped). // these constructors take ownership of the winding. @@ -377,14 +368,14 @@ struct decomp_brush_side_t /** * Returns the { front, back } after the clip. */ - std::tuple clipToPlane(const qvec3d &normal, double distance) const + std::tuple clipToPlane(const decomp_plane_t &plane) const { // FIXME: assert normal/distance are not our plane std::vector frontfaces, backfaces; for (auto &face : faces) { - auto [faceFront, faceBack] = face.clipToPlane(normal, distance); + auto [faceFront, faceBack] = face.clipToPlane(plane); if (faceFront.winding) { frontfaces.emplace_back(std::move(faceFront)); } @@ -408,14 +399,14 @@ struct decomp_brush_t /** * Returns the front and back side after clipping to the given plane. */ - std::tuple clipToPlane(const qvec3d &normal, double distance) const + std::tuple clipToPlane(const qplane3d &plane) const { // FIXME: this won't handle the the given plane is one of the brush planes std::vector frontSides, backSides; for (const auto &side : sides) { - auto [frontSide, backSide] = side.clipToPlane(normal, distance); + auto [frontSide, backSide] = side.clipToPlane({ plane }); frontSides.emplace_back(frontSide); backSides.emplace_back(backSide); } @@ -423,8 +414,8 @@ struct decomp_brush_t // NOTE: the frontSides, backSides vectors will have redundant planes at this point. Should be OK.. // Now we need to add the splitting plane itself to the sides vectors - frontSides.emplace_back(-normal, -distance); - backSides.emplace_back(normal, distance); + frontSides.emplace_back(-plane.normal, -plane.dist); + backSides.emplace_back(plane.normal, plane.dist); return {decomp_brush_t(frontSides), decomp_brush_t(backSides)}; } @@ -439,7 +430,7 @@ struct decomp_brush_t for (auto &otherSide : sides) { float distance = GLM_DistAbovePlane( - qvec4f(qvec3f(otherSide.plane.normal), (float)otherSide.plane.distance), point); + qvec4f(qvec3f(otherSide.plane.normal), (float)otherSide.plane.dist), point); if (distance > 0.1) { return false; } @@ -469,7 +460,7 @@ static decomp_brush_t BuildInitialBrush(const mbsp_t *bsp, const std::vectorplanenum); - result.node = node; - result.nodefront = front; - - const dplane_t *dplane = BSP_GetPlane(bsp, node->planenum); - - result.normal = qvec3d(dplane->normal[0], dplane->normal[1], dplane->normal[2]); - result.distance = static_cast(dplane->dist); - - // flip the plane if we went down the front side, since we want the outward-facing plane - if (front) { - result.normal = result.normal * -1.0; - result.distance = result.distance * -1.0; - } - - return result; + return { + // flip the plane if we went down the front side, since we want the outward-facing plane + front ? -dplane : dplane, + node, + front + }; } /** diff --git a/common/bspfile.cc b/common/bspfile.cc index 02c9351f..401a8a87 100644 --- a/common/bspfile.cc +++ b/common/bspfile.cc @@ -910,7 +910,7 @@ struct lump_reader buffer.reserve(length = (lump.filelen / lumpspec.size)); } else { - buffer.reserve(length = lump.filelen); + buffer.resize(length = lump.filelen); } if (!lump.filelen) diff --git a/common/bsputils.cc b/common/bsputils.cc index 78b8d95d..99d2b9f2 100644 --- a/common/bsputils.cc +++ b/common/bsputils.cc @@ -118,24 +118,19 @@ const qvec3f &Face_PointAtIndex(const mbsp_t *bsp, const mface_t *f, int v) qvec3d Face_Normal(const mbsp_t *bsp, const mface_t *f) { - return Face_Plane(bsp, f).normal(); + return Face_Plane(bsp, f).normal; } qplane3d Face_Plane(const mbsp_t *bsp, const mface_t *f) { Q_assert(f->planenum >= 0 && f->planenum < bsp->dplanes.size()); - const dplane_t *dplane = &bsp->dplanes[f->planenum]; + qplane3d result = bsp->dplanes[f->planenum]; - plane_t result; if (f->side) { - result.normal = -dplane->normal; - result.dist = -dplane->dist; - } else { - result.normal = dplane->normal; - result.dist = dplane->dist; + return -result; } - return {result.normal, result.dist}; + return result; } const gtexinfo_t *Face_Texinfo(const mbsp_t *bsp, const mface_t *face) @@ -323,6 +318,36 @@ bool Light_PointInWorld(const mbsp_t *bsp, const qvec3d &point) return Light_PointInSolid(bsp, &bsp->dmodels[0], point); } +static std::vector Face_AllocInwardFacingEdgePlanes(const mbsp_t *bsp, const mface_t *face) +{ + std::vector out; + out.reserve(face->numedges); + + const qplane3d faceplane = Face_Plane(bsp, face); + for (int i = 0; i < face->numedges; i++) { + const qvec3d &v0 = GetSurfaceVertexPoint(bsp, face, i); + const qvec3d &v1 = GetSurfaceVertexPoint(bsp, face, (i + 1) % face->numedges); + + qvec3d edgevec = qv::normalize(v1 - v0); + qvec3d normal = qv::cross(edgevec, faceplane.normal); + + out.emplace_back(normal, DotProduct(normal, v0)); + } + + return out; +} + +static bool EdgePlanes_PointInside(const std::vector &edgeplanes, const qvec3d &point) +{ + for (auto &plane : edgeplanes) { + const vec_t planedist = DotProduct(point, plane.normal) - plane.dist; + if (planedist < 0) { + return false; + } + } + return true; +} + static const mface_t *BSP_FindFaceAtPoint_r( const mbsp_t *bsp, const int nodenum, const qvec3d &point, const qvec3d &wantedNormal) { @@ -351,9 +376,8 @@ static const mface_t *BSP_FindFaceAtPoint_r( } // Next test if it's within the boundaries of the face - plane_t *edgeplanes = Face_AllocInwardFacingEdgePlanes(bsp, face); - const bool insideFace = EdgePlanes_PointInside(face, edgeplanes, point); - delete[] edgeplanes; + auto edgeplanes = Face_AllocInwardFacingEdgePlanes(bsp, face); + const bool insideFace = EdgePlanes_PointInside(edgeplanes, point); // Found a match? if (insideFace) { @@ -376,37 +400,6 @@ const mface_t *BSP_FindFaceAtPoint( return BSP_FindFaceAtPoint_r(bsp, model->headnode[0], point, wantedNormal); } -plane_t *Face_AllocInwardFacingEdgePlanes(const mbsp_t *bsp, const mface_t *face) -{ - plane_t *out = new plane_t[face->numedges]; - - const qplane3d faceplane = Face_Plane(bsp, face); - for (int i = 0; i < face->numedges; i++) { - plane_t *dest = &out[i]; - - const qvec3d &v0 = GetSurfaceVertexPoint(bsp, face, i); - const qvec3d &v1 = GetSurfaceVertexPoint(bsp, face, (i + 1) % face->numedges); - - qvec3d edgevec = qv::normalize(v1 - v0); - - CrossProduct(edgevec, faceplane.normal(), dest->normal); - dest->dist = DotProduct(dest->normal, v0); - } - - return out; -} - -bool EdgePlanes_PointInside(const mface_t *face, const plane_t *edgeplanes, const qvec3d &point) -{ - for (int i = 0; i < face->numedges; i++) { - const vec_t planedist = DotProduct(point, edgeplanes[i].normal) - edgeplanes[i].dist; - if (planedist < 0) { - return false; - } - } - return true; -} - // glm stuff std::vector GLM_FacePoints(const mbsp_t *bsp, const mface_t *face) { diff --git a/common/cmdlib.cc b/common/cmdlib.cc index 3f0b0171..088bf0bb 100644 --- a/common/cmdlib.cc +++ b/common/cmdlib.cc @@ -287,7 +287,7 @@ long LoadFile(const std::filesystem::path &filename, void *destptr) { uint8_t **bufferptr = static_cast(destptr); - qfile_t file = SafeOpenRead(filename); + qfile_t file = SafeOpenRead(filename, true); long length = Sys_FileLength(file); diff --git a/common/mathlib.cc b/common/mathlib.cc index a1a3e807..64e22d94 100644 --- a/common/mathlib.cc +++ b/common/mathlib.cc @@ -32,16 +32,6 @@ using namespace polylib; const vec3_t vec3_origin = {0, 0, 0}; -plane_t FlipPlane(plane_t input) -{ - plane_t result; - - VectorScale(input.normal, -1, result.normal); - result.dist = -input.dist; - - return result; -} - qmat3x3d RotateAboutX(double t) { // https://en.wikipedia.org/wiki/Rotation_matrix#Examples @@ -424,7 +414,7 @@ std::pair, std::vector> GLM_ClipPoly(const std::vect winding_t w = winding_t::from_winding_points(poly); - auto clipped = w.clip(qvec3f(plane), plane[3]); + auto clipped = w.clip({ plane.xyz(), plane[3] }); return make_pair( clipped[0].value_or(winding_t{}).glm_winding_points(), clipped[1].value_or(winding_t{}).glm_winding_points()); diff --git a/include/common/bspfile.hh b/include/common/bspfile.hh index 0be17d49..09d8b9a2 100644 --- a/include/common/bspfile.hh +++ b/include/common/bspfile.hh @@ -278,6 +278,11 @@ struct miptex_t virtual ~miptex_t() { } + virtual size_t stream_size() const + { + return sizeof(dmiptex_t) + width * height / 64 * 85; + } + virtual void stream_read(std::istream &stream) { auto start = stream.tellg(); @@ -291,7 +296,7 @@ struct miptex_t height = dtex.height; for (size_t g = 0; g < MIPLEVELS; g++) { - if (dtex.offsets[g] < 0) { + if (dtex.offsets[g] <= 0) { continue; } @@ -305,8 +310,6 @@ struct miptex_t virtual void stream_write(std::ostream &stream) const { - Q_assert((bool)data[0]); - std::array as_array{}; memcpy(as_array.data(), name.c_str(), name.size()); @@ -315,12 +318,18 @@ struct miptex_t uint32_t header_end = sizeof(dmiptex_t); for (size_t i = 0; i < MIPLEVELS; i++) { - stream <= header_end; - header_end += (width >> i) * (height >> i); + if (data[i] <= 0) { + stream <= (uint32_t) 0; + } else { + stream <= header_end; + header_end += (width >> i) * (height >> i); + } } for (size_t i = 0; i < MIPLEVELS; i++) { - stream.write(reinterpret_cast(data[i].get()), (width >> i) * (height >> i)); + if (data[i]) { + stream.write(reinterpret_cast(data[i].get()), (width >> i) * (height >> i)); + } } } }; @@ -335,6 +344,11 @@ struct miptexhl_t : miptex_t // convert miptex_t to miptexhl_t miptexhl_t(miptex_t &&move) : miptex_t(std::forward(move)) { } + virtual size_t stream_size() const + { + return miptex_t::stream_size() + sizeof(uint16_t) + palette.size(); + } + virtual void stream_read(std::istream &stream) { miptex_t::stream_read(stream); @@ -350,7 +364,7 @@ struct miptexhl_t : miptex_t { miptex_t::stream_write(stream); - stream <= static_cast(palette.size()); + stream <= static_cast(palette.size() / 3); stream.write(reinterpret_cast(palette.data()), palette.size()); } @@ -402,9 +416,12 @@ struct dmiptexlump_t void stream_write(std::ostream &stream) const { + auto p = (size_t) stream.tellp(); + stream <= static_cast(textures.size()); const size_t header_size = sizeof(int32_t) + (sizeof(int32_t) * textures.size()); + size_t miptex_offset = 0; for (auto &texture : textures) { @@ -413,11 +430,19 @@ struct dmiptexlump_t continue; } stream <= static_cast(header_size + miptex_offset); - miptex_offset += sizeof(dmiptex_t) + texture.width * texture.height / 64 * 85; + + miptex_offset += texture.stream_size(); + + if (p + miptex_offset % 4) { + miptex_offset += 4 - ((p + miptex_offset) % 4); + } } for (auto &texture : textures) { if (texture.name[0]) { + if (stream.tellp() % 4) { + stream.seekp(stream.tellp() + (4 - (stream.tellp() % 4))); + } texture.stream_write(stream); } } @@ -443,12 +468,12 @@ struct rgba_miptex_t #define PLANE_ANYY 4 #define PLANE_ANYZ 5 -struct dplane_t +struct dplane_t : qplane3f { - qvec3f normal; - float dist; int32_t type; + [[nodiscard]] constexpr dplane_t operator-() const { return { qplane3f::operator-(), type }; } + // serialize for streams auto stream_data() { return std::tie(normal, dist, type); } }; diff --git a/include/common/bsputils.hh b/include/common/bsputils.hh index 06193c08..e8b9061e 100644 --- a/include/common/bsputils.hh +++ b/include/common/bsputils.hh @@ -61,8 +61,6 @@ bool Light_PointInWorld(const mbsp_t *bsp, const qvec3d &point); */ const mface_t *BSP_FindFaceAtPoint( const mbsp_t *bsp, const dmodelh2_t *model, const qvec3d &point, const qvec3d &wantedNormal); -plane_t *Face_AllocInwardFacingEdgePlanes(const mbsp_t *bsp, const mface_t *face); -bool EdgePlanes_PointInside(const mface_t *face, const plane_t *edgeplanes, const qvec3d &point); const qvec3f &Face_PointAtIndex(const mbsp_t *bsp, const mface_t *f); const qvec3f &Vertex_GetPos(const mbsp_t *bsp, int num); diff --git a/include/common/mathlib.hh b/include/common/mathlib.hh index 6858abb5..b4d5aed7 100644 --- a/include/common/mathlib.hh +++ b/include/common/mathlib.hh @@ -41,10 +41,14 @@ using vec_t = double; constexpr vec_t VECT_MAX = std::numeric_limits::max(); +/* + * The quality of the bsp output is highly sensitive to these epsilon values. + */ constexpr vec_t ZERO_TRI_AREA_EPSILON = 0.05; constexpr vec_t POINT_EQUAL_EPSILON = 0.05; constexpr vec_t NORMAL_EPSILON = 0.000001; +constexpr vec_t DIST_EPSILON = 0.0001; constexpr vec_t DEGREES_EPSILON = 0.001; constexpr vec_t DEFAULT_ON_EPSILON = 0.1; diff --git a/include/common/polylib.hh b/include/common/polylib.hh index 6bcbe8b8..b81262d4 100644 --- a/include/common/polylib.hh +++ b/include/common/polylib.hh @@ -21,7 +21,7 @@ constexpr size_t MAX_POINTS_ON_WINDING = 96; constexpr vec_t DEFAULT_BOGUS_RANGE = 65536.0; -using winding_edges_t = std::vector; +using winding_edges_t = std::vector; inline bool PointInWindingEdges(const winding_edges_t &wi, const qvec3d &point) { @@ -404,29 +404,22 @@ public: *this = std::move(temp); } - plane_t plane() const + qplane3d plane() const { - plane_t p; - qvec3d v1 = at(0) - at(1); qvec3d v2 = at(2) - at(1); qvec3d normal = qv::normalize(qv::cross(v1, v2)); - for (size_t i = 0; i < 3; i++) - p.normal[i] = normal[i]; - - p.dist = qv::dot(at(0), normal); - - return p; + return { normal, qv::dot(at(0), normal) }; } - static winding_base_t from_plane(const qvec3d &normal, const vec_t &dist, const vec_t &worldextent) + static winding_base_t from_plane(const qplane3d &plane, const vec_t &worldextent) { /* find the major axis */ vec_t max = -VECT_MAX; int32_t x = -1; for (size_t i = 0; i < 3; i++) { - vec_t v = fabs(normal[i]); + vec_t v = fabs(plane.normal[i]); if (v > max) { x = i; @@ -445,12 +438,12 @@ public: case 2: vup[0] = 1; break; } - vec_t v = qv::dot(vup, normal); - vup += normal * -v; + vec_t v = qv::dot(vup, plane.normal); + vup += plane.normal * -v; vup = qv::normalize(vup); - qvec3d org = normal * dist; - qvec3d vright = qv::cross(vup, normal); + qvec3d org = plane.normal * plane.dist; + qvec3d vright = qv::cross(vup, plane.normal); vup *= worldextent; vright *= worldextent; @@ -475,7 +468,7 @@ public: if (a < 1) FError("{} area", a); - plane_t face = plane(); + qplane3d face = plane(); for (size_t i = 0; i < count; i++) { const qvec3d &p1 = at(i); @@ -520,36 +513,34 @@ public: winding_edges_t winding_edges() const { - plane_t p = plane(); + qplane3d p = plane(); - winding_edges_t result(count); + winding_edges_t result; + result.reserve(count); for (size_t i = 0; i < count; i++) { - plane_t &dest = result[i]; - const qvec3d &v0 = at(i); const qvec3d &v1 = at((i + 1) % count); qvec3d edgevec = qv::normalize(v1 - v0); qvec3d normal = qv::cross(edgevec, p.normal); - for (size_t i = 0; i < 3; i++) - dest.normal[i] = normal[i]; - dest.dist = qv::dot(normal, v0); + + result.emplace_back(normal, qv::dot(normal, v0)); } return result; } // dists/sides must have (size() + 1) reserved - inline void calc_sides(const qvec3d &normal, const vec_t &dist, vec_t *dists, side_t *sides, int32_t counts[3], + inline void calc_sides(const qplane3d &plane, vec_t *dists, side_t *sides, int32_t counts[3], const vec_t &on_epsilon = DEFAULT_ON_EPSILON) const { /* determine sides for each point */ size_t i; for (i = 0; i < count; i++) { - vec_t dot = qv::dot(at(i), normal); - dot -= dist; + vec_t dot = qv::dot(at(i), plane.normal); + dot -= plane.dist; dists[i] = dot; @@ -576,14 +567,14 @@ public: it will be clipped away. ================== */ - std::array, 2> clip(const qvec3d &normal, const vec_t &dist, + std::array, 2> clip(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)); int counts[3]{}; - calc_sides(normal, dist, dists, sides, counts, on_epsilon); + calc_sides(plane, dists, sides, counts, on_epsilon); if (keepon && !counts[SIDE_FRONT] && !counts[SIDE_BACK]) return {*this, std::nullopt}; @@ -618,10 +609,10 @@ public: qvec3d mid; for (size_t j = 0; j < 3; j++) { /* avoid round off error when possible */ - if (normal[j] == 1) - mid[j] = dist; - else if (normal[j] == -1) - mid[j] = -dist; + if (plane.normal[j] == 1) + mid[j] = plane.dist; + else if (plane.normal[j] == -1) + mid[j] = -plane.dist; else mid[j] = p1[j] + dot * (p2[j] - p1[j]); } @@ -636,16 +627,6 @@ public: return {std::move(results[SIDE_FRONT]), std::move(results[SIDE_BACK])}; } - std::optional chop( - const qvec3d &normal, const vec_t &dist, const vec_t &on_epsilon = DEFAULT_ON_EPSILON) - { - auto clipped = clip(normal, dist, on_epsilon); - - clear(); - - return clipped[0]; - } - using save_fn_t = void (*)(winding_base_t &w, void *userinfo); void dice(vec_t subdiv, save_fn_t save_fn, void *userinfo) @@ -670,10 +651,10 @@ public: // // split the winding // - qvec3d split{}; - split[i] = 1; - vec_t dist = subdiv * (1 + floor((b.mins()[i] + 1) / subdiv)); - auto clipped = clip(split, dist); + qplane3d split{}; + split.normal[i] = 1; + split.dist = subdiv * (1 + floor((b.mins()[i] + 1) / subdiv)); + auto clipped = clip(split); clear(); // diff --git a/include/common/qvec.hh b/include/common/qvec.hh index 4cb7273f..29912618 100644 --- a/include/common/qvec.hh +++ b/include/common/qvec.hh @@ -379,12 +379,17 @@ template return fmt::format("{}", v1); } +template +[[nodiscard]] bool epsilonEqual(const T &v1, const T &v2, T epsilon) +{ + return fabs(v1 - v2) <= epsilon; +} + template [[nodiscard]] bool epsilonEqual(const qvec &v1, const qvec &v2, T epsilon) { for (size_t i = 0; i < N; i++) { - T diff = v1[i] - v2[i]; - if (fabs(diff) > epsilon) + if (!epsilonEqual(v1[i], v2[i], epsilon)) return false; } return true; @@ -393,11 +398,7 @@ template template [[nodiscard]] bool epsilonEmpty(const qvec &v1, T epsilon) { - for (size_t i = 0; i < N; i++) { - if (fabs(v1[i]) > epsilon) - return false; - } - return true; + return epsilonEqual({}, v1, epsilon); } template @@ -571,21 +572,21 @@ using qvec3s = qvec; template class qplane3 { +public: + qvec normal; + T dist; + + constexpr qplane3() = default; + constexpr qplane3(const qvec &normal, const T &dist) : normal(normal), dist(dist) { } + + // convert from plane of a different type + template + constexpr qplane3(const qplane3 &plane) : qplane3(plane.normal, static_cast(plane.dist)) { } + private: - qvec m_normal; - T m_dist; + auto as_tuple() const { return std::tie(normal, dist); } public: - inline qplane3() = default; - constexpr qplane3(const qvec &normal, const T &dist) : m_normal(normal), m_dist(dist) { } - - template - constexpr qplane3(const qplane3 &plane) : qplane3(plane.normal(), plane.dist()) - { - } - - auto as_tuple() const { return std::tie(m_normal, m_dist); } - // Sort support [[nodiscard]] constexpr bool operator<(const qplane3 &other) const { return as_tuple() < other.as_tuple(); } [[nodiscard]] constexpr bool operator<=(const qplane3 &other) const { return as_tuple() <= other.as_tuple(); } @@ -594,19 +595,29 @@ public: [[nodiscard]] constexpr bool operator==(const qplane3 &other) const { return as_tuple() == other.as_tuple(); } [[nodiscard]] constexpr bool operator!=(const qplane3 &other) const { return as_tuple() != other.as_tuple(); } - [[nodiscard]] inline T distAbove(const qvec &pt) const { return qv::dot(pt, m_normal) - m_dist; } - [[nodiscard]] constexpr const qvec &normal() const { return m_normal; } - [[nodiscard]] constexpr const T dist() const { return m_dist; } + [[nodiscard]] inline T distAbove(const qvec &pt) const { return qv::dot(pt, normal) - dist; } [[nodiscard]] constexpr const qvec vec4() const { - return qvec(m_normal[0], m_normal[1], m_normal[2], m_dist); + return qvec(normal[0], normal[1], normal[2], dist); } + + [[nodiscard]] constexpr qplane3 operator-() const { return { -normal, -dist }; } }; using qplane3f = qplane3; using qplane3d = qplane3; +namespace qv +{ +template +[[nodiscard]] bool epsilonEqual(const qplane3 &p1, const qplane3 &p2, T normalEpsilon = NORMAL_EPSILON, T distEpsilon = DIST_EPSILON) +{ + return epsilonEqual(p1.normal, p2.normal, normalEpsilon) && + epsilonEqual(p1.dist, p2.dist, distEpsilon); +} +} + /** * Row x Col matrix of T. */ @@ -797,12 +808,6 @@ namespace qv // "vec3" type. legacy; eventually will be replaced entirely using vec3_t = vec_t[3]; -struct plane_t -{ - qvec3d normal; - vec_t dist; -}; - extern const vec3_t vec3_origin; template @@ -885,8 +890,6 @@ constexpr void VectorClear(T &out) out[2] = 0; } -plane_t FlipPlane(plane_t input); - template constexpr void VectorMA(const Ta &va, vec_t scale, const Tb &vb, Tc &vc) { diff --git a/include/light/light.hh b/include/light/light.hh index c7da89d1..03749ff6 100644 --- a/include/light/light.hh +++ b/include/light/light.hh @@ -112,7 +112,7 @@ struct lightsurf_t vec3_t minlight_color; bool nodirt; - plane_t plane; + qplane3d plane; vec3_t snormal; vec3_t tnormal; @@ -174,7 +174,9 @@ enum debugmode_t debugmode_bounce, debugmode_bouncelights, debugmode_debugoccluded, - debugmode_debugneighbours + debugmode_debugneighbours, + debugmode_phong_tangents, + debugmode_phong_bitangents }; extern debugmode_t debugmode; diff --git a/include/light/trace_embree.hh b/include/light/trace_embree.hh index 2572b1fb..a15e9f08 100644 --- a/include/light/trace_embree.hh +++ b/include/light/trace_embree.hh @@ -32,7 +32,7 @@ void Embree_TraceInit(const mbsp_t *bsp); hitresult_t Embree_TestSky(const qvec3d &start, const qvec3d &dirn, const modelinfo_t *self, const mface_t **face_out); hitresult_t Embree_TestLight(const qvec3d &start, const qvec3d &stop, const modelinfo_t *self); hittype_t Embree_DirtTrace(const qvec3d &start, const qvec3d &dirn, vec_t dist, const modelinfo_t *self, - vec_t *hitdist_out, plane_t *hitplane_out, const mface_t **face_out); + vec_t *hitdist_out, qplane3d *hitplane_out, const mface_t **face_out); raystream_occlusion_t *Embree_MakeOcclusionRayStream(int maxrays); raystream_intersection_t *Embree_MakeIntersectionRayStream(int maxrays); diff --git a/include/qbsp/brush.hh b/include/qbsp/brush.hh index 2b103d0b..ad0d58dc 100644 --- a/include/qbsp/brush.hh +++ b/include/qbsp/brush.hh @@ -35,7 +35,7 @@ struct brush_t class mapbrush_t; -plane_t Face_Plane(const face_t *face); +qplane3d Face_Plane(const face_t *face); int Brush_ListCountWithCFlags(const brush_t *brush, int cflags); int Brush_ListCount(const brush_t *brush); @@ -52,15 +52,6 @@ brush_t *LoadBrush(const mapentity_t *src, const mapbrush_t *mapbrush, const con const qvec3d &rotate_offset, const rotation_t rottype, const int hullnum); void FreeBrushes(mapentity_t *ent); -int FindPlane(const qvec3d &normal, const vec_t dist, int *side); -bool PlaneEqual(const plane_t *p1, const plane_t *p2); -bool PlaneInvEqual(const plane_t *p1, const plane_t *p2); - -bool BoundBrush(brush_t *brush); -vec_t BrushVolume(const brush_t *brush); -int BrushMostlyOnSide(const brush_t *brush, const qvec3d &normal, vec_t dist); -void SplitBrush(const brush_t *brush, int planenum, int planeside, brush_t **front, brush_t **back); - -void FilterStructuralBrushesIntoTree(const mapentity_t *e, node_t *headnode); +int FindPlane(const qplane3d &plane, int *side); void FreeBrush(brush_t *brush); diff --git a/include/qbsp/csg4.hh b/include/qbsp/csg4.hh index 097de623..524b72a0 100644 --- a/include/qbsp/csg4.hh +++ b/include/qbsp/csg4.hh @@ -26,5 +26,5 @@ extern int csgmergefaces; // build surfaces is also used by GatherNodeFaces surface_t *BuildSurfaces(const std::map &planefaces); face_t *NewFaceFromFace(face_t *in); -void SplitFace(face_t *in, const qbsp_plane_t *split, face_t **front, face_t **back); +void SplitFace(face_t *in, const qplane3d &split, face_t **front, face_t **back); void UpdateFaceSphere(face_t *in); diff --git a/include/qbsp/map.hh b/include/qbsp/map.hh index 5832b7ca..12986fb3 100644 --- a/include/qbsp/map.hh +++ b/include/qbsp/map.hh @@ -29,6 +29,14 @@ #include #include +struct qbsp_plane_t : qplane3d +{ + int type = 0; + std::optional outputplanenum = std::nullopt; // only valid after ExportNodePlanes + + [[nodiscard]] constexpr qbsp_plane_t operator-() const { return { qplane3d::operator-(), type }; } +}; + struct mapface_t { qbsp_plane_t plane { }; diff --git a/include/qbsp/qbsp.hh b/include/qbsp/qbsp.hh index 88bb31a5..4f872878 100644 --- a/include/qbsp/qbsp.hh +++ b/include/qbsp/qbsp.hh @@ -82,7 +82,6 @@ constexpr int32_t PLANENUM_LEAF = -1; * ( TODO: re-check if CONTINUOUS_EPSILON is still directly related ) */ #define ANGLEEPSILON 0.000001 -#define DIST_EPSILON 0.0001 #define ZERO_EPSILON 0.0001 #define DISTEPSILON 0.0001 #define POINT_EPSILON 0.0001 @@ -176,7 +175,6 @@ struct node_t // information for decision nodes int planenum; // -1 = leaf node - // outputplanenum moved to qbsp_plane_t int firstface; // decision node only int numfaces; // decision node only node_t *children[2]; // children[0] = front side, children[1] = back side of plane. only valid for decision nodes diff --git a/include/qbsp/solidbsp.hh b/include/qbsp/solidbsp.hh index 937a3fff..1c9c5f37 100644 --- a/include/qbsp/solidbsp.hh +++ b/include/qbsp/solidbsp.hh @@ -26,6 +26,5 @@ extern std::atomic splitnodes; void DetailToSolid(node_t *node); -void DivideFacet(face_t *in, qbsp_plane_t *split, face_t **front, face_t **back); void CalcSurfaceInfo(surface_t *surf); void SubdivideFace(face_t *f, face_t **prevptr); diff --git a/include/qbsp/winding.hh b/include/qbsp/winding.hh index 11f748d8..e89de399 100644 --- a/include/qbsp/winding.hh +++ b/include/qbsp/winding.hh @@ -22,14 +22,7 @@ #pragma once #include "common/polylib.hh" -#include - -struct qbsp_plane_t : plane_t -{ - int type; - std::optional outputplanenum; // only valid after ExportNodePlanes -}; using winding_t = polylib::winding_base_t; -winding_t BaseWindingForPlane(const qbsp_plane_t *p); \ No newline at end of file +winding_t BaseWindingForPlane(const qplane3d &p); \ No newline at end of file diff --git a/include/vis/vis.hh b/include/vis/vis.hh index 3dbcaa54..16d9d2f2 100644 --- a/include/vis/vis.hh +++ b/include/vis/vis.hh @@ -43,7 +43,7 @@ enum pstatus_t struct portal_t { - plane_t plane; // normal pointing into neighbor + qplane3d plane; // normal pointing into neighbor int leaf; // neighbor std::shared_ptr winding; pstatus_t status; @@ -123,7 +123,7 @@ struct winding_t : polylib::winding_base_t struct sep_t { sep_t *next; - plane_t plane; // from portal is on positive side + qplane3d plane; // from portal is on positive side }; struct passage_t @@ -153,15 +153,15 @@ struct pstack_t portal_t *portal; // portal exiting std::shared_ptr source, pass; std::shared_ptr windings[STACK_WINDINGS]; // Fixed size windings - plane_t portalplane; + qplane3d portalplane; leafbits_t *mightsee; // bit string - plane_t separators[2][MAX_SEPARATORS]; /* Separator cache */ + qplane3d separators[2][MAX_SEPARATORS]; /* Separator cache */ int numseparators[2]; }; std::shared_ptr &AllocStackWinding(pstack_t *stack); void FreeStackWinding(std::shared_ptr &w, pstack_t *stack); -std::shared_ptr ClipStackWinding(std::shared_ptr &in, pstack_t *stack, plane_t *split); +std::shared_ptr ClipStackWinding(std::shared_ptr &in, pstack_t *stack, qplane3d *split); struct threaddata_t { diff --git a/light/bounce.cc b/light/bounce.cc index 50250c45..4381f178 100644 --- a/light/bounce.cc +++ b/light/bounce.cc @@ -55,7 +55,7 @@ public: winding_t w; vec3_t center; vec3_t samplepoint; // 1 unit above center - plane_t plane; + qplane3d plane; std::map lightByStyle; }; @@ -203,7 +203,7 @@ static void *MakeBounceLightsThread(void *arg) continue; } - plane_t faceplane = winding.plane(); + qplane3d faceplane = winding.plane(); qvec3d facemidpoint = winding.center(); facemidpoint += faceplane.normal; // lift 1 unit diff --git a/light/light.cc b/light/light.cc index 5e41b465..2bc479f7 100644 --- a/light/light.cc +++ b/light/light.cc @@ -935,6 +935,46 @@ static inline void WriteNormals(const mbsp_t &bsp, bspdata_t &bspdata) } bspdata.bspx.transfer("FACENORMALS", data, data_size); + + ofstream obj("test.obj"); + + size_t index_id = 1; + + for (auto &face : bsp.dfaces) { + auto &cache = FaceCacheForFNum(&face - bsp.dfaces.data()); + /*bool keep = true; + + for (size_t i = 0; i < cache.points().size(); i++) { + auto &pt = cache.points()[i]; + + if (qv::distance(pt, { -208, 6, 21 }) > 256) { + keep = false; + break; + } + } + + if (!keep) { + continue; + }*/ + + for (size_t i = 0; i < cache.points().size(); i++) { + auto &pt = cache.points()[i]; + auto &n = cache.normals()[i]; + + fmt::print(obj, "v {}\n", pt); + fmt::print(obj, "vn {}\n", n.normal); + } + + for (size_t i = 1; i < cache.points().size() - 1; i++) { + size_t n1 = 0; + size_t n2 = i; + size_t n3 = (i + 1) % cache.points().size(); + + fmt::print(obj, "f {0}//{0} {1}//{1} {2}//{2}\n", index_id + n1, index_id + n2, index_id + n3); + } + + index_id += cache.points().size(); + } } /* diff --git a/light/ltface.cc b/light/ltface.cc index 47631453..bcdd5507 100644 --- a/light/ltface.cc +++ b/light/ltface.cc @@ -115,7 +115,7 @@ faceextents_t::faceextents_t(const mface_t *face, const mbsp_t *bsp, float lmsca " surface {}, {} extents = {}, scale = {}\n" " texture {} at ({})\n" " surface normal ({})\n", - Face_GetNum(bsp, face), i ? "t" : "s", m_texsize[i], m_lightmapscale, texname, point, plane.normal()); + Face_GetNum(bsp, face), i ? "t" : "s", m_texsize[i], m_lightmapscale, texname, point, plane.normal); } } } @@ -211,10 +211,10 @@ qmat4x4f WorldToTexSpace(const mbsp_t *bsp, const mface_t *f) // [?] qmat4x4f T{ - tex->vecs.at(0, 0), tex->vecs.at(1, 0), static_cast(plane.normal()[0]), 0, // col 0 - tex->vecs.at(0, 1), tex->vecs.at(1, 1), static_cast(plane.normal()[1]), 0, // col 1 - tex->vecs.at(0, 2), tex->vecs.at(1, 2), static_cast(plane.normal()[2]), 0, // col 2 - tex->vecs.at(0, 3), tex->vecs.at(1, 3), static_cast(-plane.dist()), 1 // col 3 + tex->vecs.at(0, 0), tex->vecs.at(1, 0), static_cast(plane.normal[0]), 0, // col 0 + tex->vecs.at(0, 1), tex->vecs.at(1, 1), static_cast(plane.normal[1]), 0, // col 1 + tex->vecs.at(0, 2), tex->vecs.at(1, 2), static_cast(plane.normal[2]), 0, // col 2 + tex->vecs.at(0, 3), tex->vecs.at(1, 3), static_cast(-plane.dist), 1 // col 3 }; return T; } @@ -758,12 +758,11 @@ static bool Lightsurf_Init( } /* Set up the plane, not including model offset */ - plane_t *plane = &lightsurf->plane; + qplane3d *plane = &lightsurf->plane; VectorCopy(bsp->dplanes[face->planenum].normal, plane->normal); plane->dist = bsp->dplanes[face->planenum].dist; if (face->side) { - VectorSubtract(vec3_origin, plane->normal, plane->normal); - plane->dist = -plane->dist; + *plane = -*plane; } /* Set up the texorg for coordinate transformation */ @@ -1405,7 +1404,7 @@ static void LightFace_Entity( { const globalconfig_t &cfg = *lightsurf->cfg; const modelinfo_t *modelinfo = lightsurf->modelinfo; - const plane_t *plane = &lightsurf->plane; + const qplane3d *plane = &lightsurf->plane; const float planedist = DotProduct(entity->origin.vec3Value(), plane->normal) - plane->dist; @@ -1518,7 +1517,7 @@ static void LightFace_Sky(const sun_t *sun, const lightsurf_t *lightsurf, lightm { const globalconfig_t &cfg = *lightsurf->cfg; const modelinfo_t *modelinfo = lightsurf->modelinfo; - const plane_t *plane = &lightsurf->plane; + const qplane3d *plane = &lightsurf->plane; // FIXME: Normalized sun vector should be stored in the sun_t. Also clarify which way the vector points (towards or // away..) diff --git a/light/trace_embree.cc b/light/trace_embree.cc index 53372293..bfd49888 100644 --- a/light/trace_embree.cc +++ b/light/trace_embree.cc @@ -423,17 +423,12 @@ static void Embree_FilterFuncN(const struct RTCFilterFunctionNArguments *args) // building faces for skip-textured bmodels -plane_t Node_Plane(const mbsp_t *bsp, const bsp2_dnode_t *node, bool side) +qplane3d Node_Plane(const mbsp_t *bsp, const bsp2_dnode_t *node, bool side) { - const dplane_t *dplane = &bsp->dplanes[node->planenum]; - plane_t plane; - - VectorCopy(dplane->normal, plane.normal); - plane.dist = dplane->dist; + qplane3d plane = bsp->dplanes[node->planenum]; if (side) { - VectorScale(plane.normal, -1, plane.normal); - plane.dist *= -1.0f; + return -plane; } return plane; @@ -443,22 +438,20 @@ plane_t Node_Plane(const mbsp_t *bsp, const bsp2_dnode_t *node, bool side) * `planes` all of the node planes that bound this leaf, facing inward. */ static void Leaf_MakeFaces( - const mbsp_t *bsp, const mleaf_t *leaf, const std::vector &planes, std::vector &result) + const mbsp_t *bsp, const mleaf_t *leaf, const std::vector &planes, std::vector &result) { - for (const plane_t &plane : planes) { + for (const qplane3d &plane : planes) { // flip the inward-facing split plane to get the outward-facing plane of the face we're constructing - plane_t faceplane; - VectorScale(plane.normal, -1, faceplane.normal); - faceplane.dist = -plane.dist; + qplane3d faceplane = -plane; - std::optional winding = winding_t::from_plane(faceplane.normal, faceplane.dist, 10e6); + std::optional winding = winding_t::from_plane(faceplane, 10e6); // clip `winding` by all of the other planes - for (const plane_t &plane2 : planes) { + for (const qplane3d &plane2 : planes) { if (&plane2 == &plane) continue; - auto clipped = winding->clip(plane2.normal, plane2.dist); + auto clipped = winding->clip(plane2); // discard the back, continue clipping the front part winding = clipped[0]; @@ -476,7 +469,7 @@ static void Leaf_MakeFaces( } } -void MakeFaces_r(const mbsp_t *bsp, const int nodenum, std::vector *planes, std::vector &result) +void MakeFaces_r(const mbsp_t *bsp, const int nodenum, std::vector *planes, std::vector &result) { if (nodenum < 0) { const int leafnum = -nodenum - 1; @@ -492,21 +485,19 @@ void MakeFaces_r(const mbsp_t *bsp, const int nodenum, std::vector *pla const bsp2_dnode_t *node = &bsp->dnodes[nodenum]; // go down the front side - const plane_t front = Node_Plane(bsp, node, false); - planes->push_back(front); + planes->push_back(Node_Plane(bsp, node, false)); MakeFaces_r(bsp, node->children[0], planes, result); planes->pop_back(); // go down the back side - const plane_t back = Node_Plane(bsp, node, true); - planes->push_back(back); + planes->push_back(Node_Plane(bsp, node, true)); MakeFaces_r(bsp, node->children[1], planes, result); planes->pop_back(); } static void MakeFaces(const mbsp_t *bsp, const dmodelh2_t *model, std::vector &result) { - std::vector planes; + std::vector planes; MakeFaces_r(bsp, model->headnode[0], &planes, result); Q_assert(planes.empty()); } @@ -725,7 +716,7 @@ hitresult_t Embree_TestSky(const qvec3d &start, const qvec3d &dirn, const modeli // public hittype_t Embree_DirtTrace(const qvec3d &start, const qvec3d &dirn, vec_t dist, const modelinfo_t *self, - vec_t *hitdist_out, plane_t *hitplane_out, const mface_t **face_out) + vec_t *hitdist_out, qplane3d *hitplane_out, const mface_t **face_out) { RTCRayHit ray = SetupRay(0, start, dirn, dist); ray_source_info ctx2(nullptr, self); diff --git a/qbsp/brush.cc b/qbsp/brush.cc index 91a4f0d9..183c4422 100644 --- a/qbsp/brush.cc +++ b/qbsp/brush.cc @@ -27,23 +27,20 @@ /* * Beveled clipping hull can generate many extra faces */ -#define MAX_FACES 128 -#define MAX_HULL_POINTS 512 -#define MAX_HULL_EDGES 1024 +constexpr size_t MAX_FACES = 128; +constexpr size_t MAX_HULL_POINTS = 512; +constexpr size_t MAX_HULL_EDGES = 1024; struct hullbrush_t { const mapbrush_t *srcbrush; contentflags_t contents; - int numfaces; aabb3d bounds; - mapface_t faces[MAX_FACES]; - int numpoints; - int numedges; - vec3_t points[MAX_HULL_POINTS]; - vec3_t corners[MAX_HULL_POINTS * 8]; - int edges[MAX_HULL_EDGES][2]; + std::vector faces; + std::vector points; + std::vector corners; + std::vector> edges; int linenum; }; @@ -53,17 +50,12 @@ struct hullbrush_t Face_Plane ================= */ -plane_t Face_Plane(const face_t *face) +qplane3d Face_Plane(const face_t *face) { - const qbsp_plane_t *plane = &map.planes.at(face->planenum); - plane_t result; - - result.dist = plane->dist; - VectorCopy(plane->normal, result.normal); + qplane3d result = map.planes.at(face->planenum); if (face->planeside) { - VectorScale(result.normal, -1.0, result.normal); - result.dist = -result.dist; + return -result; } return result; @@ -76,66 +68,64 @@ CheckFace Note: this will not catch 0 area polygons ================= */ -void CheckFace(face_t *face, const mapface_t *sourceface) +static void CheckFace(face_t *face, const mapface_t &sourceface) { - const qbsp_plane_t *plane = &map.planes[face->planenum]; - vec_t length, dist, edgedist; - vec3_t edgevec, edgenormal, facenormal; - int i, j; + const qbsp_plane_t &plane = map.planes[face->planenum]; if (face->w.size() < 3) { if (face->w.size() == 2) { - FError("line {}: too few points (2): ({}) ({})\n", sourceface->linenum, face->w[0], face->w[1]); + FError("line {}: too few points (2): ({}) ({})\n", sourceface.linenum, face->w[0], face->w[1]); } else if (face->w.size() == 1) { - FError("line {}: too few points (1): ({})\n", sourceface->linenum, face->w[0]); + FError("line {}: too few points (1): ({})\n", sourceface.linenum, face->w[0]); } else { - FError("line {}: too few points ({})", sourceface->linenum, face->w.size()); + FError("line {}: too few points ({})", sourceface.linenum, face->w.size()); } } - VectorCopy(plane->normal, facenormal); + qvec3d facenormal = plane.normal; if (face->planeside) - VectorSubtract(vec3_origin, facenormal, facenormal); + VectorInverse(facenormal); - for (i = 0; i < face->w.size(); i++) { + for (size_t i = 0; i < face->w.size(); i++) { const qvec3d &p1 = face->w[i]; const qvec3d &p2 = face->w[(i + 1) % face->w.size()]; - for (j = 0; j < 3; j++) - if (p1[j] > options.worldExtent || p1[j] < -options.worldExtent) - FError("line {}: coordinate out of range ({})", sourceface->linenum, p1[j]); + for (auto &v : p1) + if (v > options.worldExtent || v < -options.worldExtent) + FError("line {}: coordinate out of range ({})", sourceface.linenum, v); /* check the point is on the face plane */ - dist = DotProduct(p1, plane->normal) - plane->dist; + vec_t dist = DotProduct(p1, plane.normal) - plane.dist; if (dist < -ON_EPSILON || dist > ON_EPSILON) - LogPrint("WARNING: Line {}: Point ({:.3} {:.3} {:.3}) off plane by {:2.4}\n", sourceface->linenum, p1[0], + LogPrint("WARNING: Line {}: Point ({:.3} {:.3} {:.3}) off plane by {:2.4}\n", sourceface.linenum, p1[0], p1[1], p1[2], dist); /* check the edge isn't degenerate */ - VectorSubtract(p2, p1, edgevec); - length = VectorLength(edgevec); + qvec3d edgevec = p2 - p1; + vec_t length = VectorLength(edgevec); if (length < ON_EPSILON) { - LogPrint("WARNING: Line {}: Healing degenerate edge ({}) at ({:.3f} {:.3} {:.3})\n", sourceface->linenum, + LogPrint("WARNING: Line {}: Healing degenerate edge ({}) at ({:.3f} {:.3} {:.3})\n", sourceface.linenum, length, p1[0], p1[1], p1[2]); - for (j = i + 1; j < face->w.size(); j++) + for (size_t j = i + 1; j < face->w.size(); j++) VectorCopy(face->w[j], face->w[j - 1]); face->w.resize(face->w.size() - 1); CheckFace(face, sourceface); break; } + qvec3d edgenormal; CrossProduct(facenormal, edgevec, edgenormal); VectorNormalize(edgenormal); - edgedist = DotProduct(p1, edgenormal); + vec_t edgedist = DotProduct(p1, edgenormal); edgedist += ON_EPSILON; /* all other points must be on front side */ - for (j = 0; j < face->w.size(); j++) { + for (size_t j = 0; j < face->w.size(); j++) { if (j == i) continue; dist = DotProduct(face->w[j], edgenormal); if (dist > edgedist) - FError("line {}: Found a non-convex face (error size {}, point: {})\n", sourceface->linenum, + FError("line {}: Found a non-convex face (error size {}, point: {})\n", sourceface.linenum, dist - edgedist, face->w[j]); } } @@ -143,72 +133,54 @@ void CheckFace(face_t *face, const mapface_t *sourceface) //=========================================================================== -static int NormalizePlane(qbsp_plane_t *p, bool flip = true) +static bool NormalizePlane(qbsp_plane_t &p, bool flip = true) { - int i; - vec_t ax, ay, az; - - for (i = 0; i < 3; i++) { - if (p->normal[i] == 1.0) { - p->normal[(i + 1) % 3] = 0; - p->normal[(i + 2) % 3] = 0; - p->type = PLANE_X + i; + for (size_t i = 0; i < 3; i++) { + if (p.normal[i] == 1.0) { + p.normal[(i + 1) % 3] = 0; + p.normal[(i + 2) % 3] = 0; + p.type = PLANE_X + i; return 0; /* no flip */ } - if (p->normal[i] == -1.0) { + if (p.normal[i] == -1.0) { if (flip) { - p->normal[i] = 1.0; - p->dist = -p->dist; + p.normal[i] = 1.0; + p.dist = -p.dist; } - p->normal[(i + 1) % 3] = 0; - p->normal[(i + 2) % 3] = 0; - p->type = PLANE_X + i; + p.normal[(i + 1) % 3] = 0; + p.normal[(i + 2) % 3] = 0; + p.type = PLANE_X + i; return 1; /* plane flipped */ } } - ax = fabs(p->normal[0]); - ay = fabs(p->normal[1]); - az = fabs(p->normal[2]); + vec_t ax = fabs(p.normal[0]); + vec_t ay = fabs(p.normal[1]); + vec_t az = fabs(p.normal[2]); if (ax >= ay && ax >= az) - p->type = PLANE_ANYX; + p.type = PLANE_ANYX; else if (ay >= ax && ay >= az) - p->type = PLANE_ANYY; + p.type = PLANE_ANYY; else - p->type = PLANE_ANYZ; + p.type = PLANE_ANYZ; - if (flip && p->normal[p->type - PLANE_ANYX] < 0) { - VectorSubtract(vec3_origin, p->normal, p->normal); - p->dist = -p->dist; - return 1; /* plane flipped */ + if (flip && p.normal[p.type - PLANE_ANYX] < 0) { + p = -p; + return true; /* plane flipped */ } - return 0; /* no flip */ -} -bool PlaneEqual(const plane_t *p1, const plane_t *p2) -{ - return (fabs(p1->normal[0] - p2->normal[0]) < NORMAL_EPSILON && - fabs(p1->normal[1] - p2->normal[1]) < NORMAL_EPSILON && - fabs(p1->normal[2] - p2->normal[2]) < NORMAL_EPSILON && fabs(p1->dist - p2->dist) < DIST_EPSILON); -} - -bool PlaneInvEqual(const plane_t *p1, const plane_t *p2) -{ - plane_t temp{}; - VectorScale(p1->normal, -1.0, temp.normal); - temp.dist = -p1->dist; - return PlaneEqual(&temp, p2); + return false; /* no flip */ } /* Plane Hashing */ -inline int plane_hash_fn(const qbsp_plane_t *p) +inline int plane_hash_fn(const qplane3d &p) { - return Q_rint(fabs(p->dist)); + return Q_rint(fabs(p.dist)); } -static void PlaneHash_Add(const qbsp_plane_t *p, int index) +static void PlaneHash_Add(const qplane3d &p, int index) { const int hash = plane_hash_fn(p); map.planehash[hash].push_back(index); @@ -218,7 +190,7 @@ static void PlaneHash_Add(const qbsp_plane_t *p, int index) * NewPlane * - Returns a global plane number and the side that will be the front */ -static int NewPlane(const qbsp_plane_t &plane, int *side) +static int NewPlane(const qplane3d &plane, int *side) { vec_t len = VectorLength(plane.normal); @@ -226,15 +198,15 @@ static int NewPlane(const qbsp_plane_t &plane, int *side) FError("invalid normal (vector length {:.4})", len); size_t index = map.planes.size(); - qbsp_plane_t &added_plane = map.planes.emplace_back(plane); + qbsp_plane_t &added_plane = map.planes.emplace_back(qbsp_plane_t { plane }); - int32_t out_side = NormalizePlane(&added_plane, side != nullptr); + bool out_flipped = NormalizePlane(added_plane, side != nullptr); if (side) { - *side = out_side; + *side = out_flipped ? SIDE_BACK : SIDE_FRONT; } - PlaneHash_Add(&added_plane, index); + PlaneHash_Add(added_plane, index); return index; } @@ -243,20 +215,16 @@ static int NewPlane(const qbsp_plane_t &plane, int *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 qvec3d &normal, const vec_t dist, int *side) +int FindPlane(const qplane3d &plane, int *side) { - qbsp_plane_t plane {}; - plane.normal = normal; - plane.dist = dist; - - for (int i : map.planehash[plane_hash_fn(&plane)]) { + for (int i : map.planehash[plane_hash_fn(plane)]) { const qbsp_plane_t &p = map.planes.at(i); - if (PlaneEqual(&p, &plane)) { + if (qv::epsilonEqual(p, plane)) { if (side) { *side = SIDE_FRONT; } return i; - } else if (side && PlaneInvEqual(&p, &plane)) { + } else if (side && qv::epsilonEqual(-p, plane)) { *side = SIDE_BACK; return i; } @@ -334,13 +302,11 @@ CreateBrushFaces static face_t *CreateBrushFaces(const mapentity_t *src, hullbrush_t *hullbrush, const qvec3d &rotate_offset, const rotation_t rottype, const int hullnum) { - int i, j, k; vec_t r; face_t *f; std::optional w; qbsp_plane_t plane; face_t *facelist = NULL; - mapface_t *mapface, *mapface2; vec3_t point; vec_t max, min; @@ -352,27 +318,27 @@ static face_t *CreateBrushFaces(const mapentity_t *src, hullbrush_t *hullbrush, auto DiscardHintSkipFace = (options.target_game->id == GAME_QUAKE_II) ? DiscardHintSkipFace_Q2 : DiscardHintSkipFace_Q1; - mapface = hullbrush->faces; - for (i = 0; i < hullbrush->numfaces; i++, mapface++) { + for (auto &mapface : hullbrush->faces) { if (hullnum <= 0 && hullbrush->contents.is_hint()) { /* Don't generate hintskip faces */ - const mtexinfo_t &texinfo = map.mtexinfos.at(mapface->texinfo); + const mtexinfo_t &texinfo = map.mtexinfos.at(mapface.texinfo); if (DiscardHintSkipFace(hullnum, hullbrush, texinfo)) continue; } - w = BaseWindingForPlane(&mapface->plane); - mapface2 = hullbrush->faces; - for (j = 0; j < hullbrush->numfaces && w; j++, mapface2++) { - if (j == i) - continue; - // flip the plane, because we want to keep the back side - VectorSubtract(vec3_origin, mapface2->plane.normal, plane.normal); - plane.dist = -mapface2->plane.dist; + w = BaseWindingForPlane(mapface.plane); - w = w->clip(plane.normal, plane.dist, ON_EPSILON, false)[SIDE_FRONT]; + for (auto &mapface2 : hullbrush->faces) { + if (&mapface == &mapface2) + continue; + + // flip the plane, because we want to keep the back side + plane = -mapface2.plane; + + w = w->clip(plane, ON_EPSILON, false)[SIDE_FRONT]; } + if (!w) continue; // overconstrained plane @@ -381,12 +347,12 @@ static face_t *CreateBrushFaces(const mapentity_t *src, hullbrush_t *hullbrush, f->planenum = PLANENUM_LEAF; if (w->size() > MAXEDGES) - FError("face->numpoints > MAXEDGES ({}), source face on line {}", MAXEDGES, mapface->linenum); + FError("face->numpoints > MAXEDGES ({}), source face on line {}", MAXEDGES, mapface.linenum); f->w.resize(w->size()); - for (j = 0; j < w->size(); j++) { - for (k = 0; k < 3; k++) { + for (size_t j = 0; j < w->size(); j++) { + for (size_t k = 0; k < 3; k++) { point[k] = w->at(j)[k] - rotate_offset[k]; r = Q_rint(point[k]); if (fabs(point[k] - r) < ZERO_EPSILON) @@ -405,22 +371,22 @@ static face_t *CreateBrushFaces(const mapentity_t *src, hullbrush_t *hullbrush, // account for texture offset, from txqbsp-xt if (options.fixRotateObjTexture) { - mtexinfo_t texInfoNew = map.mtexinfos.at(mapface->texinfo); + mtexinfo_t texInfoNew = map.mtexinfos.at(mapface.texinfo); texInfoNew.outputnum = std::nullopt; texInfoNew.vecs.at(0, 3) += DotProduct(rotate_offset, texInfoNew.vecs.row(0)); texInfoNew.vecs.at(1, 3) += DotProduct(rotate_offset, texInfoNew.vecs.row(1)); - mapface->texinfo = FindTexinfo(texInfoNew); + mapface.texinfo = FindTexinfo(texInfoNew); } - VectorCopy(mapface->plane.normal, plane.normal); - VectorScale(mapface->plane.normal, mapface->plane.dist, point); + VectorCopy(mapface.plane.normal, plane.normal); + VectorScale(mapface.plane.normal, mapface.plane.dist, point); VectorSubtract(point, rotate_offset, point); plane.dist = DotProduct(plane.normal, point); - f->texinfo = hullnum > 0 ? 0 : mapface->texinfo; - f->planenum = FindPlane(plane.normal, plane.dist, &f->planeside); + f->texinfo = hullnum > 0 ? 0 : mapface.texinfo; + f->planenum = FindPlane(plane, &f->planeside); f->next = facelist; facelist = f; CheckFace(f, mapface); @@ -508,29 +474,25 @@ This is done by brute force, and could easily get a lot faster if anyone cares. AddBrushPlane ============= */ -static void AddBrushPlane(hullbrush_t *hullbrush, qbsp_plane_t *plane) +static void AddBrushPlane(hullbrush_t *hullbrush, const qplane3d &plane) { - int i; - mapface_t *mapface; - vec_t len; - - len = VectorLength(plane->normal); + vec_t len = VectorLength(plane.normal); if (len < 1.0 - NORMAL_EPSILON || len > 1.0 + NORMAL_EPSILON) FError("invalid normal (vector length {:.4})", len); - mapface = hullbrush->faces; - for (i = 0; i < hullbrush->numfaces; i++, mapface++) { - if (VectorCompare(mapface->plane.normal, plane->normal, EQUAL_EPSILON) && - fabs(mapface->plane.dist - plane->dist) < ON_EPSILON) + for (auto &mapface : hullbrush->faces) { + if (VectorCompare(mapface.plane.normal, plane.normal, EQUAL_EPSILON) && + fabs(mapface.plane.dist - plane.dist) < ON_EPSILON) return; } - if (hullbrush->numfaces == MAX_FACES) + + if (hullbrush->faces.size() == MAX_FACES) FError( "brush->faces >= MAX_FACES ({}), source brush on line {}", MAX_FACES, hullbrush->srcbrush->face(0).linenum); - mapface->plane = *plane; - mapface->texinfo = 0; - hullbrush->numfaces++; + mapface_t &mapface = hullbrush->faces.emplace_back(); + mapface.plane = { plane }; + mapface.texinfo = 0; } /* @@ -541,21 +503,16 @@ Adds the given plane to the brush description if all of the original brush vertexes can be put on the front side ============= */ -static void TestAddPlane(hullbrush_t *hullbrush, qbsp_plane_t *plane) +static void TestAddPlane(hullbrush_t *hullbrush, qplane3d &plane) { - int i, c; vec_t d; - mapface_t *mapface; - vec_t *corner; - qbsp_plane_t flip; int points_front, points_back; /* see if the plane has already been added */ - mapface = hullbrush->faces; - for (i = 0; i < hullbrush->numfaces; i++, mapface++) { - if (PlaneEqual(plane, &mapface->plane)) + for (auto &mapface : hullbrush->faces) { + if (qv::epsilonEqual(plane, mapface.plane)) return; - if (PlaneInvEqual(plane, &mapface->plane)) + if (qv::epsilonEqual(-plane, mapface.plane)) return; } @@ -563,11 +520,8 @@ static void TestAddPlane(hullbrush_t *hullbrush, qbsp_plane_t *plane) points_front = 0; points_back = 0; - corner = hullbrush->corners[0]; - c = hullbrush->numpoints * 8; - - for (i = 0; i < c; i++, corner += 3) { - d = DotProduct(corner, plane->normal) - plane->dist; + for (auto &corner : hullbrush->corners) { + d = DotProduct(corner, plane.normal) - plane.dist; if (d < -ON_EPSILON) { if (points_front) return; @@ -581,9 +535,7 @@ static void TestAddPlane(hullbrush_t *hullbrush, qbsp_plane_t *plane) // the plane is a seperator if (points_front) { - VectorSubtract(vec3_origin, plane->normal, flip.normal); - flip.dist = -plane->dist; - plane = &flip; + plane = -plane; } AddBrushPlane(hullbrush, plane); @@ -598,34 +550,27 @@ Doesn't add if duplicated */ static int AddHullPoint(hullbrush_t *hullbrush, const qvec3d &p, const aabb3d &hull_size) { - int i; - vec_t *c; - int x, y, z; + for (auto &pt : hullbrush->points) + if (VectorCompare(p, pt, EQUAL_EPSILON)) + return &pt - hullbrush->points.data(); - for (i = 0; i < hullbrush->numpoints; i++) - if (VectorCompare(p, hullbrush->points[i], EQUAL_EPSILON)) - return i; - - if (hullbrush->numpoints == MAX_HULL_POINTS) + if (hullbrush->points.size() == MAX_HULL_POINTS) FError("hullbrush->numpoints == MAX_HULL_POINTS ({}), " "source brush on line {}", MAX_HULL_POINTS, hullbrush->srcbrush->face(0).linenum); - VectorCopy(p, hullbrush->points[hullbrush->numpoints]); + int i = hullbrush->points.size(); + hullbrush->points.emplace_back(p); - c = hullbrush->corners[i * 8]; - - for (x = 0; x < 2; x++) - for (y = 0; y < 2; y++) - for (z = 0; z < 2; z++) { - c[0] = p[0] + hull_size[x][0]; - c[1] = p[1] + hull_size[y][1]; - c[2] = p[2] + hull_size[z][2]; - c += 3; + for (size_t x = 0; x < 2; x++) + for (size_t y = 0; y < 2; y++) + for (size_t z = 0; z < 2; z++) { + hullbrush->corners.emplace_back( + p[0] + hull_size[x][0], + p[1] + hull_size[y][1], + p[2] + hull_size[z][2]); } - hullbrush->numpoints++; - return i; } @@ -639,36 +584,33 @@ Creates all of the hull planes around the given edge, if not done allready static void AddHullEdge(hullbrush_t *hullbrush, const qvec3d &p1, const qvec3d &p2, const aabb3d &hull_size) { int pt1, pt2; - int i; int a, b, c, d, e; - vec3_t edgevec, planeorg, planevec; - qbsp_plane_t plane; + qplane3d plane; vec_t length; pt1 = AddHullPoint(hullbrush, p1, hull_size); pt2 = AddHullPoint(hullbrush, p2, hull_size); - for (i = 0; i < hullbrush->numedges; i++) - if ((hullbrush->edges[i][0] == pt1 && hullbrush->edges[i][1] == pt2) || - (hullbrush->edges[i][0] == pt2 && hullbrush->edges[i][1] == pt1)) + for (auto &edge : hullbrush->edges) + if ((edge == std::make_tuple(pt1, pt2)) || + (edge == std::make_tuple(pt2, pt1))) return; - if (hullbrush->numedges == MAX_HULL_EDGES) + if (hullbrush->edges.size() == MAX_HULL_EDGES) FError("hullbrush->numedges == MAX_HULL_EDGES ({}), " "source brush on line {}", MAX_HULL_EDGES, hullbrush->srcbrush->face(0).linenum); - hullbrush->edges[i][0] = pt1; - hullbrush->edges[i][1] = pt2; - hullbrush->numedges++; + hullbrush->edges.emplace_back(pt1, pt2); - VectorSubtract(p1, p2, edgevec); + qvec3d edgevec = p1 - p2; VectorNormalize(edgevec); for (a = 0; a < 3; a++) { b = (a + 1) % 3; c = (a + 2) % 3; + qvec3d planevec; planevec[a] = 1; planevec[b] = 0; planevec[c] = 0; @@ -682,11 +624,11 @@ static void AddHullEdge(hullbrush_t *hullbrush, const qvec3d &p1, const qvec3d & VectorScale(plane.normal, 1.0 / length, plane.normal); for (d = 0; d <= 1; d++) { for (e = 0; e <= 1; e++) { - VectorCopy(p1, planeorg); + qvec3d planeorg = p1; planeorg[b] += hull_size[d][b]; planeorg[c] += hull_size[e][c]; plane.dist = DotProduct(planeorg, plane.normal); - TestAddPlane(hullbrush, &plane); + TestAddPlane(hullbrush, plane); } } } @@ -699,54 +641,52 @@ ExpandBrush */ static void ExpandBrush(hullbrush_t *hullbrush, const aabb3d &hull_size, const face_t *facelist) { - int i, x, s; - vec3_t corner; + int x, s; const face_t *f; qbsp_plane_t plane; - mapface_t *mapface; int cBevEdge = 0; - hullbrush->numpoints = 0; - hullbrush->numedges = 0; + hullbrush->points.clear(); + hullbrush->corners.clear(); + hullbrush->edges.clear(); // create all the hull points for (f = facelist; f; f = f->next) - for (i = 0; i < f->w.size(); i++) { + for (size_t i = 0; i < f->w.size(); i++) { AddHullPoint(hullbrush, f->w[i], hull_size); cBevEdge++; } // expand all of the planes - mapface = hullbrush->faces; - for (i = 0; i < hullbrush->numfaces; i++, mapface++) { - if (mapface->flags.extended & TEX_EXFLAG_NOEXPAND) + for (auto &mapface : hullbrush->faces) { + if (mapface.flags.extended & TEX_EXFLAG_NOEXPAND) continue; - VectorCopy(vec3_origin, corner); + qvec3d corner { }; for (x = 0; x < 3; x++) { - if (mapface->plane.normal[x] > 0) + if (mapface.plane.normal[x] > 0) corner[x] = hull_size[1][x]; - else if (mapface->plane.normal[x] < 0) + else if (mapface.plane.normal[x] < 0) corner[x] = hull_size[0][x]; } - mapface->plane.dist += DotProduct(corner, mapface->plane.normal); + mapface.plane.dist += DotProduct(corner, mapface.plane.normal); } // add any axis planes not contained in the brush to bevel off corners for (x = 0; x < 3; x++) for (s = -1; s <= 1; s += 2) { // add the plane - VectorCopy(vec3_origin, plane.normal); + plane.normal = {}; plane.normal[x] = (vec_t)s; if (s == -1) plane.dist = -hullbrush->bounds.mins()[x] + -hull_size[0][x]; else plane.dist = hullbrush->bounds.maxs()[x] + hull_size[1][x]; - AddBrushPlane(hullbrush, &plane); + AddBrushPlane(hullbrush, plane); } // add all of the edge bevels for (f = facelist; f; f = f->next) - for (i = 0; i < f->w.size(); i++) + for (size_t i = 0; i < f->w.size(); i++) AddHullEdge(hullbrush, f->w[i], f->w[(i + 1) % f->w.size()], hull_size); } @@ -891,9 +831,9 @@ brush_t *LoadBrush(const mapentity_t *src, const mapbrush_t *mapbrush, const con hullbrush.contents = contents; hullbrush.srcbrush = mapbrush; - hullbrush.numfaces = mapbrush->numfaces; + hullbrush.faces.reserve(mapbrush->numfaces); for (int i = 0; i < mapbrush->numfaces; i++) - hullbrush.faces[i] = mapbrush->face(i); + hullbrush.faces.emplace_back(mapbrush->face(i)); if (hullnum <= 0) { // for hull 0 or BSPX -wrbrushes collision, apply the rotation offset now @@ -1255,476 +1195,3 @@ void Brush_LoadEntity(mapentity_t *dst, const mapentity_t *src, const int hullnu LogPercent(i + 1, src->nummapbrushes); } } - -//============================================================ - -/* -================== -BoundBrush - -Sets the mins/maxs based on the windings -returns false if the brush doesn't enclose a valid volume - -from q3map -================== -*/ -bool BoundBrush(brush_t *brush) -{ - brush->bounds = {}; - - for (face_t *face = brush->faces; face; face = face->next) { - const winding_t &w = face->w; - for (int j = 0; j < w.size(); j++) - brush->bounds += w[j]; - } - - for (int i = 0; i < 3; i++) { - // CHECK: because aabb::fix, is this latter check ever possible? - if (brush->bounds.mins()[i] < MIN_WORLD_COORD || brush->bounds.maxs()[i] > MAX_WORLD_COORD || - brush->bounds.mins()[i] >= brush->bounds.maxs()[i]) { - return false; - } - } - - return true; -} - -/* -================== -BrushVolume - -from q3map -modified to follow https://en.wikipedia.org/wiki/Polyhedron#Volume -================== -*/ -vec_t BrushVolume(const brush_t *brush) -{ - if (!brush) - return 0; - - vec_t volume = 0; - for (const face_t *face = brush->faces; face; face = face->next) { - if (!face->w.size()) - continue; - - const vec_t area = face->w.area(); - const plane_t faceplane = Face_Plane(face); - - volume += DotProduct(faceplane.normal, face->w[0]) * area; - } - - volume /= 3.0; - - return volume; -} - -/* -================== -BrushMostlyOnSide - -from q3map -================== -*/ -int BrushMostlyOnSide(const brush_t *brush, const qvec3d &planenormal, vec_t planedist) -{ - vec_t max; - int side; - - max = 0; - side = SIDE_FRONT; - - for (const face_t *face = brush->faces; face; face = face->next) { - const winding_t &w = face->w; - - if (!w.size()) - continue; - - for (int j = 0; j < w.size(); j++) { - const vec_t d = DotProduct(w[j], planenormal) - planedist; - if (d > max) { - max = d; - side = SIDE_FRONT; - } - if (-d > max) { - max = -d; - side = SIDE_BACK; - } - } - } - - return side; -} - -face_t *CopyFace(const face_t *face) -{ - face_t *newface = new face_t(*face); - - // clear stuff that shouldn't be copied. - newface->original = nullptr; - newface->outputnumber = std::nullopt; - newface->edges.clear(); - newface->next = nullptr; - - return newface; -} - -/* -================== -CopyBrush - -from q3map - -Duplicates the brush, the sides, and the windings -================== -*/ -brush_t *CopyBrush(const brush_t *brush) -{ - brush_t *newbrush = new brush_t(*brush); - - newbrush->next = nullptr; - newbrush->faces = nullptr; - - for (const face_t *face = brush->faces; face; face = face->next) { - - face_t *newface = CopyFace(face); - - // link into newbrush - newface->next = newbrush->faces; - newbrush->faces = newface; - } - - return newbrush; -} - -/* -================ -WindingIsTiny - -Returns true if the winding would be crunched out of -existance by the vertex snapping. - -from q3map -================ -*/ -#define EDGE_LENGTH 0.2 -static bool WindingIsTiny(const winding_t &w) -{ - /* - if (WindingArea (w) < 1) - return qtrue; - return qfalse; - */ - size_t edges = 0; - - for (size_t i = 0; i < w.size(); i++) { - size_t j = i == w.size() - 1 ? 0 : i + 1; - qvec3d delta = w[j] - w[i]; - vec_t len = VectorLength(delta); - - if (len > EDGE_LENGTH) { - if (++edges == 3) - return false; - } - } - - return true; -} - -/* -================ -WindingIsHuge - -Returns true if the winding still has one of the points -from basewinding for plane - -from q3map -================ -*/ -static bool WindingIsHuge(const winding_t &w) -{ - for (size_t i = 0; i < w.size(); i++) - for (size_t j = 0; j < 3; j++) - if (w[i][j] <= MIN_WORLD_COORD || w[i][j] >= MAX_WORLD_COORD) - return true; - - return false; -} - -/* -================ -SplitBrush - -Generates two new brushes, leaving the original -unchanged - -from q3map -================ -*/ -void SplitBrush(const brush_t *brush, int planenum, int planeside, brush_t **front, brush_t **back) -{ - *front = nullptr; - *back = nullptr; - - qbsp_plane_t plane; - { - const qbsp_plane_t *globalplane = &map.planes.at(planenum); - VectorCopy(globalplane->normal, plane.normal); - plane.dist = globalplane->dist; - if (planeside) { - VectorScale(plane.normal, -1, plane.normal); - plane.dist = -plane.dist; - } - // FIXME: dangerous.. - plane.type = -1000; - plane.outputplanenum = std::nullopt; - } - - // check all points - vec_t d_front = 0; - vec_t d_back = 0; - for (const face_t *face = brush->faces; face; face = face->next) { - const winding_t &w = face->w; - if (!w.size()) - continue; - - for (int j = 0; j < w.size(); j++) { - const vec_t d = DotProduct(w[j], plane.normal) - plane.dist; - if (d > 0 && d > d_front) - d_front = d; - if (d < 0 && d < d_back) - d_back = d; - } - } - - if (d_front < 0.1) // PLANESIDE_EPSILON) - { // only on back - *back = CopyBrush(brush); - return; - } - if (d_back > -0.1) // PLANESIDE_EPSILON) - { // only on front - *front = CopyBrush(brush); - return; - } - - // create a new winding from the split plane - std::optional w = BaseWindingForPlane(&plane); - - for (const face_t *face = brush->faces; face; face = face->next) { - if (!w) - break; - const plane_t plane2 = FlipPlane(Face_Plane(face)); - w = w->chop(plane2.normal, plane2.dist, 0); // PLANESIDE_EPSILON); - } - - if (!w || WindingIsTiny(*w)) { // the brush isn't really split - int side; - - side = BrushMostlyOnSide(brush, plane.normal, plane.dist); - if (side == SIDE_FRONT) - *front = CopyBrush(brush); - if (side == SIDE_BACK) - *back = CopyBrush(brush); - return; - } - - if (WindingIsHuge(*w)) { - LogPrint("WARNING: huge winding\n"); - } - - brush_t *b[2]; - - // split it for real - - // first, make two empty brushes (for the front and back side of the plane) - - for (int i = 0; i < 2; i++) { - b[i] = new brush_t{}; - // memcpy( b[i], brush, sizeof( brush_t ) ); - - // NOTE: brush copying - b[i]->contents = brush->contents; - b[i]->lmshift = brush->lmshift; - b[i]->faces = nullptr; - b[i]->next = nullptr; - - // FIXME: - // b[i]->original = brush->original; - } - - // split all the current windings - - for (const face_t *face = brush->faces; face; face = face->next) { - const winding_t &w = face->w; - if (!w.size()) - continue; - - auto cw = w.clip(plane.normal, plane.dist, ON_EPSILON); - - for (int j = 0; j < 2; j++) { - if (!cw[j]) - continue; - /* - if (WindingIsTiny (cw[j])) - { - FreeWinding (cw[j]); - continue; - } - */ - - face_t *newface = CopyFace(face); - newface->w = std::move(*cw[j]); - UpdateFaceSphere(newface); - - // link it into the front or back brush we are building - newface->next = b[j]->faces; - b[j]->faces = newface; - } - } - - // see if we have valid polygons on both sides - - for (int i = 0; i < 2; i++) { - bool bad_brush = false; - - if (!BoundBrush(b[i])) { - LogPrint("bogus brush after clip\n"); - bad_brush = true; - } - - // 3 faces is ok because we add a 4th face below - if (Brush_NumFaces(b[i]) < 3 || bad_brush) { - FreeBrush(b[i]); - b[i] = nullptr; - } - } - - if (!(b[0] && b[1])) { - if (!b[0] && !b[1]) - LogPrint("split removed brush\n"); - else - LogPrint("split not on both sides\n"); - if (b[0]) { - FreeBrush(b[0]); - *front = CopyBrush(brush); - } - if (b[1]) { - FreeBrush(b[1]); - *back = CopyBrush(brush); - } - return; - } - - // add the midwinding to both sides - for (int i = 0; i < 2; i++) { - // clone the first face (arbitrarily) - face_t *newface = CopyFace(b[i]->faces); - - if (i == 0) { - newface->w = w->flip(); - newface->planenum = planenum; - newface->planeside = !planeside; - } else { - newface->w = *w; - newface->planenum = planenum; - newface->planeside = planeside; - } - - UpdateFaceSphere(newface); - - // link it into the front or back brush - newface->next = b[i]->faces; - b[i]->faces = newface; - } - - { - vec_t v1; - int i; - - for (i = 0; i < 2; i++) { - v1 = BrushVolume(b[i]); - if (v1 < 1.0) { - FreeBrush(b[i]); - b[i] = nullptr; - LogPrint("tiny volume after clip\n"); - } - } - } - - *front = b[0]; - *back = b[1]; -} - -#if 0 -/* -==================== -FilterBrushIntoTree_r - -from q3map - -returns the number of fragments the brush was split into -frees brush -==================== -*/ -int FilterBrushIntoTree_r( brush_t *b, node_t *node ) -{ - if ( !b ) { - return 0; - } - - // add it to the leaf list - if ( node->planenum == PLANENUM_LEAF ) { - - Q_assert(b->next == nullptr); - b->next = node->q3map_brushlist; - node->q3map_brushlist = b; - - // FIXME: set node->q3map_contents - - return 1; - } - - // split it by the node plane - brush_t *front, *back; - SplitBrush ( b, node->planenum, 0, &front, &back ); - FreeBrush( b ); - - int c = 0; - c += FilterBrushIntoTree_r( front, node->children[0] ); - c += FilterBrushIntoTree_r( back, node->children[1] ); - - return c; -} - -/* -===================== -FilterStructuralBrushesIntoTree - -Mark the leafs as opaque and areaportals - -from q3map -===================== -*/ -void FilterStructuralBrushesIntoTree( const mapentity_t *e, node_t *headnode ) -{ - LogPrint( "----- FilterStructuralBrushesIntoTree -----\n"); - - auto st = I_FloatTime(); - - int c_unique = 0; - int c_clusters = 0; - for ( const brush_t *b = e->brushes ; b ; b = b->next ) { - c_unique++; - brush_t *newb = CopyBrush( b ); - - int r = FilterBrushIntoTree_r( newb, headnode ); - c_clusters += r; - - // mark all sides as visible so drawsurfs are created - } - - LogPrint( "{:5} structural brushes\n", c_unique ); - LogPrint( "{:5} cluster references\n", c_clusters ); - LogPrint( "took {} seconds\n", (I_FloatTime() - st).count() ); -} -#endif diff --git a/qbsp/csg4.cc b/qbsp/csg4.cc index d351089e..f0272dbf 100644 --- a/qbsp/csg4.cc +++ b/qbsp/csg4.cc @@ -111,7 +111,7 @@ SplitFace Frees in. ================== */ -void SplitFace(face_t *in, const qbsp_plane_t *split, face_t **front, face_t **back) +void SplitFace(face_t *in, const qplane3d &split, face_t **front, face_t **back) { vec_t *dists = (vec_t *)alloca(sizeof(vec_t) * (in->w.size() + 1)); side_t *sides = (side_t *)alloca(sizeof(side_t) * (in->w.size() + 1)); @@ -125,7 +125,7 @@ void SplitFace(face_t *in, const qbsp_plane_t *split, face_t **front, face_t **b Error("Attempting to split freed face"); /* Fast test */ - dot = DotProduct(in->origin, split->normal) - split->dist; + dot = DotProduct(in->origin, split.normal) - split.dist; if (dot > in->radius) { counts[SIDE_FRONT] = 1; counts[SIDE_BACK] = 0; @@ -133,7 +133,7 @@ void SplitFace(face_t *in, const qbsp_plane_t *split, face_t **front, face_t **b counts[SIDE_FRONT] = 0; counts[SIDE_BACK] = 1; } else { - in->w.calc_sides(split->normal, split->dist, dists, sides, counts, ON_EPSILON); + in->w.calc_sides(split, dists, sides, counts, ON_EPSILON); } // Plane doesn't split this face after all @@ -177,10 +177,10 @@ void SplitFace(face_t *in, const qbsp_plane_t *split, face_t **front, face_t **b dot = dists[i] / (dists[i] - dists[i + 1]); for (j = 0; j < 3; j++) { // avoid round off error when possible - if (split->normal[j] == 1) - mid[j] = split->dist; - else if (split->normal[j] == -1) - mid[j] = -split->dist; + if (split.normal[j] == 1) + mid[j] = split.dist; + else if (split.normal[j] == -1) + mid[j] = -split.dist; else mid[j] = p1[j] + dot * (p2[j] - p1[j]); } @@ -216,10 +216,9 @@ static void RemoveOutsideFaces(const brush_t *brush, face_t **inside, face_t **o for (const face_t *clipface = brush->faces; clipface; clipface = clipface->next) { qbsp_plane_t clipplane = map.planes[clipface->planenum]; if (!clipface->planeside) { - VectorSubtract(vec3_origin, clipplane.normal, clipplane.normal); - clipplane.dist = -clipplane.dist; + clipplane = -clipplane; } - w = w->clip(clipplane.normal, clipplane.dist, ON_EPSILON, true)[SIDE_FRONT]; + w = w->clip(clipplane, ON_EPSILON, true)[SIDE_FRONT]; if (!w) break; } @@ -248,9 +247,8 @@ Faces exactly on the plane will stay inside unless overdrawn by later brush static void ClipInside(const face_t *clipface, bool precedence, face_t **inside, face_t **outside) { face_t *face, *next, *frags[2]; - const qbsp_plane_t *splitplane; - splitplane = &map.planes[clipface->planenum]; + const qbsp_plane_t &splitplane = map.planes[clipface->planenum]; face = *inside; *inside = NULL; @@ -265,7 +263,7 @@ static void ClipInside(const face_t *clipface, bool precedence, face_t **inside, vec_t *dists = (vec_t *)malloc(sizeof(vec_t) * (face->w.size() + 1)); side_t *sides = (side_t *)malloc(sizeof(side_t) * (face->w.size() + 1)); int counts[3]{}; - face->w.calc_sides(splitplane->normal, splitplane->dist, dists, sides, counts, ON_EPSILON); + face->w.calc_sides(splitplane, dists, sides, counts, ON_EPSILON); free(dists); free(sides); @@ -276,8 +274,8 @@ static void ClipInside(const face_t *clipface, bool precedence, face_t **inside, /* Handle exactly on-plane faces */ if (face->planenum == clipface->planenum || spurious_onplane) { - const plane_t faceplane = Face_Plane(face); - const plane_t clipfaceplane = Face_Plane(clipface); + const qplane3d faceplane = Face_Plane(face); + const qplane3d clipfaceplane = Face_Plane(clipface); const vec_t dp = DotProduct(faceplane.normal, clipfaceplane.normal); const bool opposite = (dp < 0); diff --git a/qbsp/map.cc b/qbsp/map.cc index 43a1d86d..de4c26ce 100644 --- a/qbsp/map.cc +++ b/qbsp/map.cc @@ -1651,12 +1651,12 @@ mapbrush_t ParseBrush(parser_t &parser, const mapentity_t *entity) bool discardFace = false; for (int i = 0; i < brush.numfaces; i++) { const mapface_t &check = brush.face(i); - if (PlaneEqual(&check.plane, &face->plane)) { + if (qv::epsilonEqual(check.plane, face->plane)) { LogPrint("line {}: Brush with duplicate plane", parser.linenum); discardFace = true; continue; } - if (PlaneInvEqual(&check.plane, &face->plane)) { + if (qv::epsilonEqual(-check.plane, face->plane)) { /* FIXME - this is actually an invalid brush */ LogPrint("line {}: Brush with duplicate plane", parser.linenum); continue; @@ -2296,12 +2296,12 @@ void WriteBspBrushMap(const std::filesystem::path &name, const std::vectorfaces; face; face = face->next) { // FIXME: Factor out this mess qbsp_plane_t plane = map.planes.at(face->planenum); + if (face->planeside) { - VectorScale(plane.normal, -1, plane.normal); - plane.dist = -plane.dist; + plane = -plane; } - winding_t w = BaseWindingForPlane(&plane); + winding_t w = BaseWindingForPlane(plane); fmt::print(f, "( {} ) ", w[0]); fmt::print(f, "( {} ) ", w[1]); diff --git a/qbsp/merge.cc b/qbsp/merge.cc index ce00d1b7..e6089e6f 100644 --- a/qbsp/merge.cc +++ b/qbsp/merge.cc @@ -100,7 +100,7 @@ static face_t *TryMerge(face_t *f1, face_t *f2) plane = &map.planes[f1->planenum]; VectorCopy(plane->normal, planenormal); if (f1->planeside) - VectorSubtract(vec3_origin, planenormal, planenormal); + VectorInverse(planenormal); back = f1->w[(i + f1->w.size() - 1) % f1->w.size()]; VectorSubtract(p1, back, delta); diff --git a/qbsp/portals.cc b/qbsp/portals.cc index a8fa150e..a3c45dd0 100644 --- a/qbsp/portals.cc +++ b/qbsp/portals.cc @@ -102,7 +102,7 @@ static void WritePortals_r(node_t *node, std::ofstream &portalFile, bool cluster std::optional w; const qbsp_plane_t *pl; int i, front, back; - plane_t plane2; + qplane3d plane2; if (node->planenum != PLANENUM_LEAF && !node->detail_separator) { WritePortals_r(node->children[0], portalFile, clusters); @@ -356,7 +356,7 @@ static void MakeHeadnodePortals(const mapentity_t *entity, node_t *node) { int i, j, n; portal_t *p, *portals[6]; - qbsp_plane_t bplanes[6], *pl; + qbsp_plane_t bplanes[6]; int side; // pad with some space so there will never be null volume leafs @@ -375,16 +375,16 @@ static void MakeHeadnodePortals(const mapentity_t *entity, node_t *node) p = new portal_t{}; portals[n] = p; - pl = &bplanes[n]; - memset(pl, 0, sizeof(*pl)); + qplane3d &pl = bplanes[n] = {}; + if (j) { - pl->normal[i] = -1; - pl->dist = -bounds[j][i]; + pl.normal[i] = -1; + pl.dist = -bounds[j][i]; } else { - pl->normal[i] = 1; - pl->dist = bounds[j][i]; + pl.normal[i] = 1; + pl.dist = bounds[j][i]; } - p->planenum = FindPlane(pl->normal, pl->dist, &side); + p->planenum = FindPlane(pl, &side); p->winding = BaseWindingForPlane(pl); if (side) @@ -399,7 +399,7 @@ static void MakeHeadnodePortals(const mapentity_t *entity, node_t *node) if (j == i) continue; portals[i]->winding = - portals[i]->winding->clip(bplanes[j].normal, bplanes[j].dist, ON_EPSILON, true)[SIDE_FRONT]; + portals[i]->winding->clip(bplanes[j], ON_EPSILON, true)[SIDE_FRONT]; } } } @@ -496,7 +496,6 @@ CutNodePortals_r */ static void CutNodePortals_r(node_t *node, portal_state_t *state) { - const qbsp_plane_t *plane; qbsp_plane_t clipplane; node_t *front, *back, *other_node; portal_t *portal, *new_portal, *next_portal; @@ -514,7 +513,7 @@ static void CutNodePortals_r(node_t *node, portal_state_t *state) if (node->detail_separator) return; - plane = &map.planes[node->planenum]; + const qbsp_plane_t &plane = map.planes[node->planenum]; front = node->children[SIDE_FRONT]; back = node->children[SIDE_BACK]; @@ -531,13 +530,12 @@ static void CutNodePortals_r(node_t *node, portal_state_t *state) if (portal->nodes[0] == node) side = SIDE_FRONT; else if (portal->nodes[1] == node) { - clipplane.dist = -clipplane.dist; - VectorSubtract(vec3_origin, clipplane.normal, clipplane.normal); + clipplane = -clipplane; side = SIDE_BACK; } else FError("Mislinked portal"); - winding = winding->clip(clipplane.normal, clipplane.dist, ON_EPSILON, true)[SIDE_FRONT]; + winding = winding->clip(clipplane, ON_EPSILON, true)[SIDE_FRONT]; if (!winding) { FLogPrint("WARNING: New portal was clipped away near ({:.3} {:.3} {:.3})\n", portal->winding->at(0)[0], portal->winding->at(0)[1], portal->winding->at(0)[2]); @@ -566,7 +564,7 @@ static void CutNodePortals_r(node_t *node, portal_state_t *state) RemovePortalFromNode(portal, portal->nodes[1]); /* cut the portal into two portals, one on each side of the cut plane */ - auto windings = portal->winding->clip(plane->normal, plane->dist, ON_EPSILON); + auto windings = portal->winding->clip(plane, ON_EPSILON); if (!windings[SIDE_FRONT]) { if (side == SIDE_FRONT) diff --git a/qbsp/qbsp.cc b/qbsp/qbsp.cc index 3d2613be..000c3a89 100644 --- a/qbsp/qbsp.cc +++ b/qbsp/qbsp.cc @@ -127,10 +127,7 @@ static std::vector> AddBrushBevels(const brush_t *b int32_t planenum = f->planenum; if (f->planeside) { - auto flipped = map.planes[f->planenum]; - flipped.dist = -flipped.dist; - VectorInverse(flipped.normal); - planenum = FindPlane(flipped.normal, flipped.dist, nullptr); + planenum = FindPlane(-map.planes[f->planenum], nullptr); } int32_t outputplanenum = ExportMapPlane(planenum); @@ -152,15 +149,14 @@ static std::vector> AddBrushBevels(const brush_t *b if (i == planes.size()) { // add a new side - plane_t new_plane; - VectorClear(new_plane.normal); + qplane3d new_plane { }; new_plane.normal[axis] = dir; if (dir == 1) new_plane.dist = b->bounds.maxs()[axis]; else new_plane.dist = -b->bounds.mins()[axis]; - int32_t planenum = FindPlane(new_plane.normal, new_plane.dist, nullptr); + int32_t planenum = FindPlane(new_plane, nullptr); int32_t outputplanenum = ExportMapPlane(planenum); planes.emplace_back(outputplanenum, b->faces); } @@ -206,7 +202,7 @@ static std::vector> AddBrushBevels(const brush_t *b // construct a plane VectorClear(vec2); vec2[axis] = dir; - plane_t current; + qplane3d current; CrossProduct(vec, vec2, current.normal); if (VectorNormalize(current.normal) < 0.5) continue; @@ -218,17 +214,10 @@ static std::vector> AddBrushBevels(const brush_t *b // behind this plane, it is a proper edge bevel for (f = b->faces; f; f = f->next) { auto &plane = map.planes[f->planenum]; - plane_t temp; - VectorCopy(plane.normal, temp.normal); - temp.dist = plane.dist; - - if (f->planeside) { - temp.dist = -temp.dist; - VectorInverse(temp.normal); - } + qplane3d temp = f->planeside ? -plane : plane; // if this plane has allready been used, skip it - if (PlaneEqual(¤t, &temp)) + if (qv::epsilonEqual(current, temp)) break; auto &w2 = f->w; @@ -248,7 +237,7 @@ static std::vector> AddBrushBevels(const brush_t *b continue; // wasn't part of the outer hull // add this plane - int32_t planenum = FindPlane(current.normal, current.dist, nullptr); + int32_t planenum = FindPlane(current, nullptr); int32_t outputplanenum = ExportMapPlane(planenum); planes.emplace_back(outputplanenum, b->faces); } @@ -494,9 +483,9 @@ static void EmitAreaPortals(node_t *headnode) } #endif -winding_t BaseWindingForPlane(const qbsp_plane_t *p) +winding_t BaseWindingForPlane(const qplane3d &p) { - return winding_t::from_plane(p->normal, p->dist, options.worldExtent); + return winding_t::from_plane(p, options.worldExtent); } /* diff --git a/qbsp/solidbsp.cc b/qbsp/solidbsp.cc index 44174069..6c12c489 100644 --- a/qbsp/solidbsp.cc +++ b/qbsp/solidbsp.cc @@ -99,22 +99,22 @@ FaceSide For BSP hueristic ================== */ -static int FaceSide__(const face_t *in, const qbsp_plane_t *split) +static int FaceSide__(const face_t *in, const qbsp_plane_t &split) { bool have_front, have_back; int i; have_front = have_back = false; - if (split->type < 3) { + if (split.type < 3) { /* shortcut for axial planes */ - const vec_t *p = &in->w[0][split->type]; + const vec_t *p = &in->w[0][split.type]; for (i = 0; i < in->w.size(); i++, p += 3) { - if (*p > split->dist + ON_EPSILON) { + if (*p > split.dist + ON_EPSILON) { if (have_back) return SIDE_ON; have_front = true; - } else if (*p < split->dist - ON_EPSILON) { + } else if (*p < split.dist - ON_EPSILON) { if (have_front) return SIDE_ON; have_back = true; @@ -124,7 +124,7 @@ static int FaceSide__(const face_t *in, const qbsp_plane_t *split) /* sloping planes take longer */ const vec_t *p = &in->w[0][0]; for (i = 0; i < in->w.size(); i++, p += 3) { - const vec_t dot = DotProduct(p, split->normal) - split->dist; + const vec_t dot = DotProduct(p, split.normal) - split.dist; if (dot > ON_EPSILON) { if (have_back) return SIDE_ON; @@ -145,20 +145,16 @@ static int FaceSide__(const face_t *in, const qbsp_plane_t *split) return SIDE_ON; } -static int FaceSide(const face_t *in, const qbsp_plane_t *split) +inline int FaceSide(const face_t *in, const qbsp_plane_t &split) { - vec_t dist; - int ret; + vec_t dist = DotProduct(in->origin, split.normal) - split.dist; - dist = DotProduct(in->origin, split->normal) - split->dist; if (dist > in->radius) - ret = SIDE_FRONT; + return SIDE_FRONT; else if (dist < -in->radius) - ret = SIDE_BACK; + return SIDE_BACK; else - ret = FaceSide__(in, split); - - return ret; + return FaceSide__(in, split); } /* @@ -167,7 +163,7 @@ static int FaceSide(const face_t *in, const qbsp_plane_t *split) * on that side of the plane. Therefore, if the split plane is * non-axial, then the returned bounds will overlap. */ -static void DivideBounds(const aabb3d &in_bounds, const qbsp_plane_t *split, aabb3d &front_bounds, aabb3d &back_bounds) +static void DivideBounds(const aabb3d &in_bounds, const qbsp_plane_t &split, aabb3d &front_bounds, aabb3d &back_bounds) { int a, b, c, i, j; vec_t dist1, dist2, mid, split_mins, split_maxs; @@ -175,17 +171,17 @@ static void DivideBounds(const aabb3d &in_bounds, const qbsp_plane_t *split, aab front_bounds = back_bounds = in_bounds; - if (split->type < 3) { + if (split.type < 3) { // CHECK: this escapes the immutability "sandbox" of aabb3d, is this a good idea? // it'd take like 6 lines to otherwise reproduce this line. - front_bounds[0][split->type] = back_bounds[1][split->type] = split->dist; + front_bounds[0][split.type] = back_bounds[1][split.type] = split.dist; return; } /* Make proper sloping cuts... */ for (a = 0; a < 3; ++a) { /* Check for parallel case... no intersection */ - if (fabs(split->normal[a]) < NORMAL_EPSILON) + if (fabs(split.normal[a]) < NORMAL_EPSILON) continue; b = (a + 1) % 3; @@ -199,10 +195,10 @@ static void DivideBounds(const aabb3d &in_bounds, const qbsp_plane_t *split, aab corner[c] = in_bounds[j][c]; corner[a] = in_bounds[0][a]; - dist1 = DotProduct(corner, split->normal) - split->dist; + dist1 = DotProduct(corner, split.normal) - split.dist; corner[a] = in_bounds[1][a]; - dist2 = DotProduct(corner, split->normal) - split->dist; + dist2 = DotProduct(corner, split.normal) - split.dist; mid = in_bounds[1][a] - in_bounds[0][a]; mid *= (dist1 / (dist1 - dist2)); @@ -212,7 +208,7 @@ static void DivideBounds(const aabb3d &in_bounds, const qbsp_plane_t *split, aab split_maxs = min(max(mid, split_maxs), in_bounds.maxs()[a]); } } - if (split->normal[a] > 0) { + if (split.normal[a] > 0) { front_bounds[0][a] = split_mins; back_bounds[1][a] = split_maxs; } else { @@ -225,12 +221,12 @@ static void DivideBounds(const aabb3d &in_bounds, const qbsp_plane_t *split, aab /* * Calculate the split plane metric for axial planes */ -static vec_t SplitPlaneMetric_Axial(const qbsp_plane_t *p, const aabb3d &bounds) +inline vec_t SplitPlaneMetric_Axial(const qbsp_plane_t &p, const aabb3d &bounds) { vec_t value = 0; for (int i = 0; i < 3; i++) { - if (i == p->type) { - const vec_t dist = p->dist * p->normal[i]; + if (i == p.type) { + const vec_t dist = p.dist * p.normal[i]; value += (bounds.maxs()[i] - dist) * (bounds.maxs()[i] - dist); value += (dist - bounds.mins()[i]) * (dist - bounds.mins()[i]); } else { @@ -244,7 +240,7 @@ static vec_t SplitPlaneMetric_Axial(const qbsp_plane_t *p, const aabb3d &bounds) /* * Calculate the split plane metric for non-axial planes */ -static vec_t SplitPlaneMetric_NonAxial(const qbsp_plane_t *p, const aabb3d &bounds) +inline vec_t SplitPlaneMetric_NonAxial(const qbsp_plane_t &p, const aabb3d &bounds) { aabb3d f, b; vec_t value = 0.0; @@ -258,9 +254,9 @@ static vec_t SplitPlaneMetric_NonAxial(const qbsp_plane_t *p, const aabb3d &boun return value; } -inline vec_t SplitPlaneMetric(const qbsp_plane_t *p, const aabb3d &bounds) +inline vec_t SplitPlaneMetric(const qbsp_plane_t &p, const aabb3d &bounds) { - if (p->type < 3) + if (p.type < 3) return SplitPlaneMetric_Axial(p, bounds); else return SplitPlaneMetric_NonAxial(p, bounds); @@ -290,8 +286,8 @@ static surface_t *ChooseMidPlaneFromList(surface_t *surfaces, const aabb3d &boun continue; /* check for axis aligned surfaces */ - const qbsp_plane_t *plane = &map.planes[surf->planenum]; - if (!(plane->type < 3)) + const qbsp_plane_t &plane = map.planes[surf->planenum]; + if (!(plane.type < 3)) continue; /* calculate the split metric, smaller values are better */ @@ -313,7 +309,7 @@ static surface_t *ChooseMidPlaneFromList(surface_t *surfaces, const aabb3d &boun if (!surf->has_struct && !pass) continue; - const qbsp_plane_t *plane = &map.planes[surf->planenum]; + const qbsp_plane_t &plane = map.planes[surf->planenum]; const vec_t metric = SplitPlaneMetric(plane, bounds); if (metric < bestmetric) { bestmetric = metric; @@ -377,14 +373,14 @@ static surface_t *ChoosePlaneFromList(surface_t *surfaces, const aabb3d &bounds) if (!surf->has_struct && !pass) continue; - const qbsp_plane_t *plane = &map.planes[surf->planenum]; + const qbsp_plane_t &plane = map.planes[surf->planenum]; int splits = 0; for (surface_t *surf2 = surfaces; surf2; surf2 = surf2->next) { if (surf2 == surf || surf2->onnode) continue; - const qbsp_plane_t *plane2 = &map.planes[surf2->planenum]; - if (plane->type < 3 && plane->type == plane2->type) + const qbsp_plane_t &plane2 = map.planes[surf2->planenum]; + if (plane.type < 3 && plane.type == plane2.type) continue; for (const face_t *face = surf2->faces; face; face = face->next) { const surfflags_t &flags = map.mtexinfos.at(face->texinfo).flags; @@ -412,8 +408,8 @@ static surface_t *ChoosePlaneFromList(surface_t *surfaces, const aabb3d &bounds) * if equal numbers axial planes win, otherwise decide on spatial * subdivision */ - if (splits < minsplits || (splits == minsplits && plane->type < 3)) { - if (plane->type < 3) { + if (splits < minsplits || (splits == minsplits && plane.type < 3)) { + if (plane.type < 3) { const vec_t distribution = SplitPlaneMetric(plane, bounds); if (distribution > bestdistribution && splits == minsplits) continue; @@ -542,15 +538,15 @@ void CalcSurfaceInfo(surface_t *surf) DividePlane ================== */ -static void DividePlane(surface_t *in, const qbsp_plane_t *split, surface_t **front, surface_t **back) +static void DividePlane(surface_t *in, const qplane3d &split, surface_t **front, surface_t **back) { const qbsp_plane_t *inplane = &map.planes[in->planenum]; *front = *back = NULL; // parallel case is easy - if (VectorCompare(inplane->normal, split->normal, EQUAL_EPSILON)) { + if (VectorCompare(inplane->normal, split.normal, EQUAL_EPSILON)) { // check for exactly on node - if (inplane->dist == split->dist) { + if (inplane->dist == split.dist) { face_t *facet = in->faces; in->faces = NULL; in->onnode = true; @@ -590,7 +586,7 @@ static void DividePlane(surface_t *in, const qbsp_plane_t *split, surface_t **fr return; } - if (inplane->dist > split->dist) + if (inplane->dist > split.dist) *front = in; else *back = in; @@ -649,7 +645,7 @@ static void DividePlane(surface_t *in, const qbsp_plane_t *split, surface_t **fr DivideNodeBounds ================== */ -inline void DivideNodeBounds(node_t *node, const qbsp_plane_t *split) +inline void DivideNodeBounds(node_t *node, const qbsp_plane_t &split) { DivideBounds(node->bounds, split, node->children[0]->bounds, node->children[1]->bounds); } @@ -807,7 +803,7 @@ static void PartitionSurfaces(surface_t *surfaces, node_t *node) node->planenum = split->planenum; node->detail_separator = split->detail_separator; - const qbsp_plane_t *splitplane = &map.planes[split->planenum]; + const qbsp_plane_t &splitplane = map.planes[split->planenum]; DivideNodeBounds(node, splitplane); diff --git a/qbsp/surfaces.cc b/qbsp/surfaces.cc index 1cb073bd..004a5dd6 100644 --- a/qbsp/surfaces.cc +++ b/qbsp/surfaces.cc @@ -103,7 +103,7 @@ void SubdivideFace(face_t *f, face_t **prevptr) plane.dist = (mins + subdiv - 16) / v; next = f->next; - SplitFace(f, &plane, &front, &back); + SplitFace(f, plane, &front, &back); if (!front || !back) { printf("didn't split\n"); break; diff --git a/qbsp/test_qbsp.cc b/qbsp/test_qbsp.cc index 8016adcf..515db759 100644 --- a/qbsp/test_qbsp.cc +++ b/qbsp/test_qbsp.cc @@ -153,56 +153,6 @@ static brush_t *load128x128x32Brush() return brush; } -TEST(qbsp, BrushVolume) -{ - brush_t *brush = load128x128x32Brush(); - - EXPECT_DOUBLE_EQ((128 * 128 * 32), BrushVolume(brush)); -} - -TEST(qbsp, BrushMostlyOnSide1) -{ - brush_t *brush = load128x128x32Brush(); - - qvec3d plane1normal {-1, 0, 0}; - vec_t plane1dist = -100; - - EXPECT_EQ(SIDE_FRONT, BrushMostlyOnSide(brush, plane1normal, plane1dist)); - - FreeBrush(brush); -} - -TEST(qbsp, BrushMostlyOnSide2) -{ - brush_t *brush = load128x128x32Brush(); - - qvec3d plane1normal {1, 0, 0}; - vec_t plane1dist = 100; - - EXPECT_EQ(SIDE_BACK, BrushMostlyOnSide(brush, plane1normal, plane1dist)); - - FreeBrush(brush); -} - -TEST(qbsp, BoundBrush) -{ - brush_t *brush = load128x128x32Brush(); - - brush->bounds = {}; - - EXPECT_EQ(true, BoundBrush(brush)); - - EXPECT_DOUBLE_EQ(-64, brush->bounds.mins()[0]); - EXPECT_DOUBLE_EQ(-64, brush->bounds.mins()[1]); - EXPECT_DOUBLE_EQ(-16, brush->bounds.mins()[2]); - - EXPECT_DOUBLE_EQ(64, brush->bounds.maxs()[0]); - EXPECT_DOUBLE_EQ(64, brush->bounds.maxs()[1]); - EXPECT_DOUBLE_EQ(16, brush->bounds.maxs()[2]); - - FreeBrush(brush); -} - static void checkForAllCubeNormals(const brush_t *brush) { const vec3_t wanted[6] = {{-1, 0, 0}, {1, 0, 0}, {0, -1, 0}, {0, 1, 0}, {0, 0, -1}, {0, 0, 1}}; @@ -213,7 +163,7 @@ static void checkForAllCubeNormals(const brush_t *brush) } for (const face_t *face = brush->faces; face; face = face->next) { - const plane_t faceplane = Face_Plane(face); + const qplane3d faceplane = Face_Plane(face); for (int i = 0; i < 6; i++) { if (VectorCompare(wanted[i], faceplane.normal, NORMAL_EPSILON)) { @@ -237,82 +187,6 @@ static void checkCube(const brush_t *brush) EXPECT_EQ(contentflags_t{CONTENTS_SOLID}, brush->contents); } -TEST(qbsp, SplitBrush) -{ - brush_t *brush = load128x128x32Brush(); - - const qvec3d planenormal {-1, 0, 0}; - int planeside; - const int planenum = FindPlane(planenormal, 0.0, &planeside); - - brush_t *front, *back; - SplitBrush(brush, planenum, planeside, &front, &back); - - ASSERT_NE(nullptr, front); - ASSERT_NE(nullptr, back); - - // front - EXPECT_DOUBLE_EQ(-64, front->bounds.mins()[0]); - EXPECT_DOUBLE_EQ(-64, front->bounds.mins()[1]); - EXPECT_DOUBLE_EQ(-16, front->bounds.mins()[2]); - - EXPECT_DOUBLE_EQ(0, front->bounds.maxs()[0]); - EXPECT_DOUBLE_EQ(64, front->bounds.maxs()[1]); - EXPECT_DOUBLE_EQ(16, front->bounds.maxs()[2]); - - checkCube(front); - - // back - EXPECT_DOUBLE_EQ(0, back->bounds.mins()[0]); - EXPECT_DOUBLE_EQ(-64, back->bounds.mins()[1]); - EXPECT_DOUBLE_EQ(-16, back->bounds.mins()[2]); - - EXPECT_DOUBLE_EQ(64, back->bounds.maxs()[0]); - EXPECT_DOUBLE_EQ(64, back->bounds.maxs()[1]); - EXPECT_DOUBLE_EQ(16, back->bounds.maxs()[2]); - - checkCube(back); - - FreeBrush(brush); - delete front; - delete back; -} - -TEST(qbsp, SplitBrushOnSide) -{ - brush_t *brush = load128x128x32Brush(); - - const qvec3d planenormal {-1, 0, 0}; - int planeside; - const int planenum = FindPlane(planenormal, -64.0, &planeside); - - brush_t *front, *back; - SplitBrush(brush, planenum, planeside, &front, &back); - - ASSERT_NE(nullptr, front); - checkCube(front); - - EXPECT_EQ(nullptr, back); -} - -#if 0 -TEST(qbsp, MemLeaks) { - brush_t *brush = load128x128x32Brush(); - - const qvec3d planenormal { -1, 0, 0 }; - int planeside; - const int planenum = FindPlane(planenormal, 0.0, &planeside); - - for (int i=0; i<1000000; i++) { - brush_t *front, *back; - SplitBrush(brush, planenum, planeside, &front, &back); - - FreeBrush(front); - FreeBrush(back); - } -} -#endif - /** * Test that this skip face gets auto-corrected. */ diff --git a/vis/flow.cc b/vis/flow.cc index 2b25bdc9..77e2049d 100644 --- a/vis/flow.cc +++ b/vis/flow.cc @@ -28,11 +28,11 @@ static int c_leafskip; pointer, was measurably faster ============== */ -static void ClipToSeparators(const std::shared_ptr &source, const plane_t src_pl, +static void ClipToSeparators(const std::shared_ptr &source, const qplane3d src_pl, const std::shared_ptr &pass, std::shared_ptr &target, unsigned int test, pstack_t *stack) { int i, j, k, l; - plane_t sep; + qplane3d sep; vec3_t v1, v2; vec_t d; int count; @@ -76,8 +76,7 @@ static void ClipToSeparators(const std::shared_ptr &source, const pla // flip the plane if the source portal is backwards // if (fliptest) { - VectorSubtract(vec3_origin, sep.normal, sep.normal); - sep.dist = -sep.dist; + sep = -sep; } // // if all of the pass portal points are now on the positive side, @@ -102,8 +101,7 @@ static void ClipToSeparators(const std::shared_ptr &source, const pla // flip the normal if we want the back side (tests 1 and 3) // if (test & 1) { - VectorSubtract(vec3_origin, sep.normal, sep.normal); - sep.dist = -sep.dist; + sep = -sep; } /* Cache separating planes for tests 0, 1 */ @@ -146,7 +144,7 @@ static void RecursiveLeafFlow(int leafnum, threaddata_t *thread, pstack_t *prevs { pstack_t stack; portal_t *p; - plane_t backplane; + qplane3d backplane; leaf_t *leaf; int i, j, err, numblocks; @@ -222,8 +220,7 @@ static void RecursiveLeafFlow(int leafnum, threaddata_t *thread, pstack_t *prevs } // get plane of portal, point normal into the neighbor leaf stack.portalplane = p->plane; - VectorSubtract(vec3_origin, p->plane.normal, backplane.normal); - backplane.dist = -p->plane.dist; + backplane = -p->plane; if (VectorCompare(prevstack->portalplane.normal, backplane.normal, EQUAL_EPSILON)) continue; // can't go out a coplanar face diff --git a/vis/vis.cc b/vis/vis.cc index 6897b342..553e5691 100644 --- a/vis/vis.cc +++ b/vis/vis.cc @@ -162,7 +162,7 @@ void FreeStackWinding(std::shared_ptr &w, pstack_t *stack) is returned. ================== */ -std::shared_ptr ClipStackWinding(std::shared_ptr &in, pstack_t *stack, plane_t *split) +std::shared_ptr ClipStackWinding(std::shared_ptr &in, pstack_t *stack, qplane3d *split) { vec_t *dists = (vec_t *)alloca(sizeof(vec_t) * (in->size() + 1)); int *sides = (int *)alloca(sizeof(int) * (in->size() + 1)); @@ -714,196 +714,6 @@ void CalcVis(mbsp_t *bsp) } } -/* - ============================================================================ - PASSAGE CALCULATION (not used yet...) - ============================================================================ -*/ -#if 0 -int count_sep; - -bool PlaneCompare(plane_t *p1, plane_t *p2) -{ - int i; - - if (fabs(p1->dist - p2->dist) > 0.01) - return false; - - for (i = 0; i < 3; i++) - if (fabs(p1->normal[i] - p2->normal[i]) > 0.001) - return false; - - return true; -} - -sep_t *Findpassages(winding_t *source, winding_t *pass) -{ - int i, j, k, l; - plane_t plane; - vec3_t v1, v2; - float d; - double length; - int counts[3]; - bool fliptest; - sep_t *sep, *list; - - list = NULL; - - // check all combinations - for (i = 0; i < source->numpoints; i++) { - l = (i + 1) % source->numpoints; - VectorSubtract(source->points[l], source->points[i], v1); - - // fing a vertex of pass that makes a plane that puts all of the - // vertexes of pass on the front side and all of the vertexes of - // source on the back side - for (j = 0; j < pass->numpoints; j++) { - VectorSubtract(pass->points[j], source->points[i], v2); - - plane.normal[0] = v1[1] * v2[2] - v1[2] * v2[1]; - plane.normal[1] = v1[2] * v2[0] - v1[0] * v2[2]; - plane.normal[2] = v1[0] * v2[1] - v1[1] * v2[0]; - - // if points don't make a valid plane, skip it - - length = plane.normal[0] * plane.normal[0] + plane.normal[1] * plane.normal[1] + - plane.normal[2] * plane.normal[2]; - - if (length < ON_EPSILON) - continue; - - length = 1 / sqrt(length); - - plane.normal[0] *= (vec_t)length; - plane.normal[1] *= (vec_t)length; - plane.normal[2] *= (vec_t)length; - - plane.dist = DotProduct(pass->points[j], plane.normal); - - // - // find out which side of the generated seperating plane has the - // source portal - // - fliptest = false; - for (k = 0; k < source->numpoints; k++) { - if (k == i || k == l) - continue; - d = DotProduct(source->points[k], plane.normal) - plane.dist; - if (d < -ON_EPSILON) { // source is on the negative side, so we want all - // pass and target on the positive side - fliptest = false; - break; - } else if (d > ON_EPSILON) { // source is on the positive side, so we want all - // pass and target on the negative side - fliptest = true; - break; - } - } - if (k == source->numpoints) - continue; // planar with source portal - - // - // flip the normal if the source portal is backwards - // - if (fliptest) { - VectorSubtract(vec3_origin, plane.normal, plane.normal); - plane.dist = -plane.dist; - } - // - // if all of the pass portal points are now on the positive side, - // this is the seperating plane - // - counts[0] = counts[1] = counts[2] = 0; - for (k = 0; k < pass->numpoints; k++) { - if (k == j) - continue; - d = DotProduct(pass->points[k], plane.normal) - plane.dist; - if (d < -ON_EPSILON) - break; - else if (d > ON_EPSILON) - counts[0]++; - else - counts[2]++; - } - if (k != pass->numpoints) - continue; // points on negative side, not a seperating plane - - if (!counts[0]) - continue; // planar with pass portal - - // - // save this out - // - count_sep++; - - sep = new sep_t; - sep->next = list; - list = sep; - sep->plane = plane; - } - } - - return list; -} - -/* - ============ - CalcPassages - ============ -*/ -void CalcPassages(void) -{ - int i, j, k; - int count, count2; - leaf_t *l; - portal_t *p1, *p2; - sep_t *sep; - passage_t *passages; - - LogPrint("building passages...\n"); - - count = count2 = 0; - for (i = 0; i < portalleafs; i++) { - l = &leafs[i]; - - for (j = 0; j < l->numportals; j++) { - p1 = l->portals[j]; - for (k = 0; k < l->numportals; k++) { - if (k == j) - continue; - - count++; - p2 = l->portals[k]; - - // definately can't see into a coplanar portal - if (PlaneCompare(&p1->plane, &p2->plane)) - continue; - - count2++; - - sep = Findpassages(p1->winding, p2->winding); - if (!sep) { - // Error ("No seperating planes found in portal pair"); - count_sep++; - sep = new sep_t; - sep->next = NULL; - sep->plane = p1->plane; - } - passages = new passage_t; - passages->planes = sep; - passages->from = p1->leaf; - passages->to = p2->leaf; - passages->next = l->passages; - l->passages = passages; - } - } - } - - LogPrint("numpassages: {} ({})\n", count2, count); - LogPrint("total passages: {}\n", count_sep); -} -#endif - // =========================================================================== /* @@ -920,7 +730,7 @@ static void LoadPortals(const std::filesystem::path &name, mbsp_t *bsp) qfile_t f{nullptr, nullptr}; int numpoints; int leafnums[2]; - plane_t plane; + qplane3d plane; if (name == "-") f = {stdin, nullptr}; @@ -1027,8 +837,7 @@ static void LoadPortals(const std::filesystem::path &name, mbsp_t *bsp) l->portals[l->numportals] = p; l->numportals++; - VectorSubtract(vec3_origin, plane.normal, p->plane.normal); - p->plane.dist = -plane.dist; + p->plane = -plane; p->leaf = leafnums[1]; p->winding->SetWindingSphere(); p++; @@ -1196,8 +1005,6 @@ int main(int argc, char **argv) uncompressed_q2 = new uint8_t[portalleafs * leafbytes]{}; } - // CalcPassages (); - CalcVis(&bsp); LogPrint("c_noclip: {}\n", c_noclip);