diff --git a/common/mathlib.cc b/common/mathlib.cc index 1db71728..c56c20fa 100644 --- a/common/mathlib.cc +++ b/common/mathlib.cc @@ -598,7 +598,7 @@ std::pair GLM_ClosestPointOnPolyBoundary(const std::vector } std::pair GLM_InterpolateNormal( - const std::vector &points, const std::vector &normals, const qvec3f &point) + const std::vector &points, const std::vector &normals, const qvec3f &point) { Q_assert(points.size() == normals.size()); @@ -608,14 +608,14 @@ std::pair GLM_InterpolateNormal( // Step through the triangles, being careful to handle zero-size ones const qvec3f &p0 = points.at(0); - const qvec3f &n0 = normals.at(0); + const qvec3f &n0 = normals.at(0).normal; const int N = points.size(); for (int i = 2; i < N; i++) { const qvec3f &p1 = points.at(i - 1); - const qvec3f &n1 = normals.at(i - 1); + const qvec3f &n1 = normals.at(i - 1).normal; const qvec3f &p2 = points.at(i); - const qvec3f &n2 = normals.at(i); + const qvec3f &n2 = normals.at(i).normal; const auto edgeplanes = GLM_MakeInwardFacingEdgePlanes({p0, p1, p2}); if (edgeplanes.size() != 3) diff --git a/include/common/mathlib.hh b/include/common/mathlib.hh index 0cfa7c89..21dd3f72 100644 --- a/include/common/mathlib.hh +++ b/include/common/mathlib.hh @@ -241,6 +241,12 @@ inline vec_t GetDir(const Tstart &start, const Tstop &stop, Tdir &dir) return VectorNormalize(dir); } +// Stores a normal, tangent and bitangents +struct face_normal_t +{ + qvec3f normal, tangent, bitangent; +}; + bool SetPlanePts(const std::array &planepts, qvec3d &normal, vec_t &dist); // Maps uniform random variables U and V in [0, 1] to uniformly distributed points on a sphere @@ -297,7 +303,7 @@ qvec4f GLM_PolyPlane(const std::vector &points); std::pair GLM_ClosestPointOnPolyBoundary(const std::vector &poly, const qvec3f &point); /// Returns `true` and the interpolated normal if `point` is in the polygon, otherwise returns false. std::pair GLM_InterpolateNormal( - const std::vector &points, const std::vector &normals, const qvec3f &point); + const std::vector &points, const std::vector &normals, const qvec3f &point); std::vector GLM_ShrinkPoly(const std::vector &poly, const float amount); /// Returns (front part, back part) std::pair, std::vector> GLM_ClipPoly(const std::vector &poly, const qvec4f &plane); diff --git a/include/light/phong.hh b/include/light/phong.hh index 19a28c18..d569d477 100644 --- a/include/light/phong.hh +++ b/include/light/phong.hh @@ -44,11 +44,10 @@ public: }; void CalculateVertexNormals(const mbsp_t *bsp); -const qvec3f GetSurfaceVertexNormal(const mbsp_t *bsp, const mface_t *f, const int vertindex); +const face_normal_t &GetSurfaceVertexNormal(const mbsp_t *bsp, const mface_t *f, const int vertindex); bool FacesSmoothed(const mface_t *f1, const mface_t *f2); const std::set &GetSmoothFaces(const mface_t *face); const std::vector &GetPlaneFaces(const mface_t *face); -const qvec3f GetSurfaceVertexNormal(const mbsp_t *bsp, const mface_t *f, const int v); const mface_t *Face_EdgeIndexSmoothed(const mbsp_t *bsp, const mface_t *f, const int edgeindex); /// a directed edge can be used by more than one face, e.g. two cube touching just along an edge @@ -62,27 +61,22 @@ class face_cache_t { private: std::vector m_points; - std::vector m_normals; - std::vector m_tangents; - std::vector m_bitangents; + std::vector m_normals; qvec4f m_plane; std::vector m_edgePlanes; std::vector m_pointsShrunkBy1Unit; std::vector m_neighbours; public: - face_cache_t(const mbsp_t *bsp, const mface_t *face, const std::vector &normals, - const std::vector &tangents, const std::vector &bitangents) - : m_points(GLM_FacePoints(bsp, face)), m_normals(normals), m_tangents(tangents), m_bitangents(bitangents), + face_cache_t(const mbsp_t *bsp, const mface_t *face, const std::vector &normals) + : m_points(GLM_FacePoints(bsp, face)), m_normals(normals), m_plane(Face_Plane(bsp, face).vec4()), m_edgePlanes(GLM_MakeInwardFacingEdgePlanes(m_points)), m_pointsShrunkBy1Unit(GLM_ShrinkPoly(m_points, 1.0f)), m_neighbours(NeighbouringFaces_new(bsp, face)) { } const std::vector &points() const { return m_points; } - const std::vector &normals() const { return m_normals; } - const std::vector &tangents() const { return m_tangents; } - const std::vector &bitangents() const { return m_bitangents; } + const std::vector &normals() const { return m_normals; } const qvec4f &plane() const { return m_plane; } const qvec3f normal() const { return m_plane; } const std::vector &edgePlanes() const { return m_edgePlanes; } diff --git a/light/light.cc b/light/light.cc index a9f15d24..6c0a8c14 100644 --- a/light/light.cc +++ b/light/light.cc @@ -539,7 +539,7 @@ static void ExportObjFace(std::ofstream &f, const mbsp_t *bsp, const mface_t *fa // export the vertices and uvs for (int i = 0; i < face->numedges; i++) { const int vertnum = Face_VertexAtIndex(bsp, face, i); - const qvec3f normal = GetSurfaceVertexNormal(bsp, face, i); + const qvec3f normal = GetSurfaceVertexNormal(bsp, face, i).normal; const qvec3f &pos = bsp->dvertexes[vertnum]; fmt::print(f, "v {:.9} {:.9} {:.9}\n", pos[0], pos[1], pos[2]); fmt::print(f, "vn {:.9} {:.9} {:.9}\n", normal[0], normal[1], normal[2]); @@ -903,10 +903,12 @@ static inline void WriteNormals(const mbsp_t &bsp, bspdata_t &bspdata) for (auto &face : bsp.dfaces) { auto &cache = FaceCacheForFNum(&face - bsp.dfaces.data()); - unique_normals.insert(cache.normals().begin(), cache.normals().end()); - unique_normals.insert(cache.tangents().begin(), cache.tangents().end()); - unique_normals.insert(cache.bitangents().begin(), cache.bitangents().end()); - num_normals += cache.normals().size() + cache.tangents().size() + cache.bitangents().size(); + for (auto &normals : cache.normals()) { + unique_normals.insert(normals.normal); + unique_normals.insert(normals.tangent); + unique_normals.insert(normals.bitangent); + num_normals += 3; + } } size_t data_size = sizeof(uint32_t) + (sizeof(qvec3f) * unique_normals.size()) + (sizeof(uint32_t) * num_normals); @@ -916,23 +918,20 @@ static inline void WriteNormals(const mbsp_t &bsp, bspdata_t &bspdata) stream << endianness; stream <= numeric_cast(unique_normals.size()); + std::map mapped_normals; + for (auto &n : unique_normals) { stream <= std::tie(n[0], n[1], n[2]); + mapped_normals.emplace(n, mapped_normals.size()); } for (auto &face : bsp.dfaces) { auto &cache = FaceCacheForFNum(&face - bsp.dfaces.data()); for (auto &n : cache.normals()) { - stream <= numeric_cast(std::distance(unique_normals.begin(), unique_normals.find(n))); - } - - for (auto &n : cache.tangents()) { - stream <= numeric_cast(std::distance(unique_normals.begin(), unique_normals.find(n))); - } - - for (auto &n : cache.bitangents()) { - stream <= numeric_cast(std::distance(unique_normals.begin(), unique_normals.find(n))); + stream <= numeric_cast(mapped_normals[n.normal]); + stream <= numeric_cast(mapped_normals[n.tangent]); + stream <= numeric_cast(mapped_normals[n.bitangent]); } } diff --git a/light/ltface.cc b/light/ltface.cc index 8056de1f..d856b218 100644 --- a/light/ltface.cc +++ b/light/ltface.cc @@ -267,7 +267,7 @@ void PrintFaceInfo(const mface_t *face, const mbsp_t *bsp) int edge = bsp->dsurfedges[face->firstedge + i]; int vert = Face_VertexAtIndex(bsp, face, i); const qvec3f &point = GetSurfaceVertexPoint(bsp, face, i); - const qvec3f norm = GetSurfaceVertexNormal(bsp, face, i); + const qvec3f norm = GetSurfaceVertexNormal(bsp, face, i).normal; LogPrint("{} {:3} ({:3.3}, {:3.3}, {:3.3}) :: normal ({:3.3}, {:3.3}, {:3.3}) :: edge {}\n", i ? " " : " verts ", vert, point[0], point[1], point[2], norm[0], norm[1], norm[2], edge); } diff --git a/light/phong.cc b/light/phong.cc index a5e40dfb..aa15c107 100644 --- a/light/phong.cc +++ b/light/phong.cc @@ -170,9 +170,7 @@ static float AngleBetweenPoints(const qvec3f &p1, const qvec3f &p2, const qvec3f } static bool s_builtPhongCaches; -static std::map> vertex_normals; -static std::map> vertex_tangents; -static std::map> vertex_bitangents; +static std::map> vertex_normals; static std::set interior_verts; static map> smoothFaces; static map> vertsToFaces; @@ -240,7 +238,7 @@ const std::vector &GetPlaneFaces(const mface_t *face) // Adapted from https://github.com/NVIDIAGameWorks/donut/blob/main/src/engine/GltfImporter.cpp#L684 std::tuple compute_tangents( - const std::array &positions, const std::array &tex_coords, const qvec3f &normal) + const std::array &positions, const std::array &tex_coords) { qvec3f dPds = positions[1] - positions[0]; qvec3f dPdt = positions[2] - positions[0]; @@ -255,16 +253,15 @@ std::tuple compute_tangents( return {qv::normalize(tangent), qv::normalize(bitangent)}; } -constexpr qvec2d uvs(const texvecf &vecs, const qvec3d &pos, const int32_t &width, const int32_t &height) +constexpr qvec2f uvs(const texvecf &vecs, const qvec3f &pos, const int32_t &width, const int32_t &height) { return {(pos[0] * vecs[0][0] + pos[1] * vecs[0][1] + pos[2] * vecs[0][2] + vecs[0][3]) / width, - (pos[0] * vecs[1][0] + pos[1] * vecs[1][1] + pos[2] * vecs[1][2] + vecs[1][3]) / height}; + (pos[0] * vecs[1][0] + pos[1] * vecs[1][1] + pos[2] * vecs[1][2] + vecs[1][3]) / height}; } /* given a triangle, just adds the contribution from the triangle to the given vertexes normals, based upon angles at * the verts. v1, v2, v3 are global vertex indices */ -static void AddTriangleNormals(std::map &smoothed_normals, std::map &smoothed_tangents, - std::map &smoothed_bitangents, const gtexinfo_t *texinfo, const rgba_miptex_t *miptex, +static void AddTriangleNormals(std::map &smoothed_normals, const gtexinfo_t *texinfo, const rgba_miptex_t *miptex, const qvec3d &norm, const mbsp_t *bsp, int v1, int v2, int v3) { const qvec3f &p1 = Vertex_GetPos(bsp, v1); @@ -281,69 +278,45 @@ static void AddTriangleNormals(std::map &smoothed_normals, std::map auto uv2 = uvs(texinfo->vecs, p2, miptex->width, miptex->height); auto uv3 = uvs(texinfo->vecs, p3, miptex->width, miptex->height); - auto tangent = compute_tangents({p1, p2, p3}, {uv1, uv2, uv3}, norm); + auto tangent = compute_tangents({p1, p2, p3}, {uv1, uv2, uv3}); weight = AngleBetweenPoints(p2, p1, p3); weight *= areaweight; - smoothed_normals[v1] += norm * weight; - smoothed_tangents[v1] += std::get<0>(tangent) * weight; - smoothed_bitangents[v1] += std::get<1>(tangent) * weight; + auto &n1 = smoothed_normals[v1]; + n1.normal += norm * weight; + n1.tangent += std::get<0>(tangent) * weight; + n1.bitangent += std::get<1>(tangent) * weight; weight = AngleBetweenPoints(p1, p2, p3); weight *= areaweight; - smoothed_normals[v2] += norm * weight; - smoothed_tangents[v2] += std::get<0>(tangent) * weight; - smoothed_bitangents[v2] += std::get<1>(tangent) * weight; + auto &n2 = smoothed_normals[v2]; + n2.normal += norm * weight; + n2.tangent += std::get<0>(tangent) * weight; + n2.bitangent += std::get<1>(tangent) * weight; weight = AngleBetweenPoints(p1, p3, p2); weight *= areaweight; - smoothed_normals[v3] += norm * weight; - smoothed_tangents[v3] += std::get<0>(tangent) * weight; - smoothed_bitangents[v3] += std::get<1>(tangent) * weight; + auto &n3 = smoothed_normals[v3]; + n3.normal += norm * weight; + n3.tangent += std::get<0>(tangent) * weight; + n3.bitangent += std::get<1>(tangent) * weight; } /* access the final phong-shaded vertex normal */ -const qvec3f GetSurfaceVertexNormal(const mbsp_t *bsp, const mface_t *f, const int vertindex) +const face_normal_t &GetSurfaceVertexNormal(const mbsp_t *bsp, const mface_t *f, const int vertindex) { Q_assert(s_builtPhongCaches); // handle degenerate faces const auto it = vertex_normals.find(f); if (it == vertex_normals.end()) { - return {}; + static const face_normal_t empty {}; + return empty; } const auto &face_normals_vec = it->second; return face_normals_vec.at(vertindex); } -/* access the final phong-shaded vertex tangent */ -static const qvec3f GetSurfaceVertexTangent(const mbsp_t *bsp, const mface_t *f, const int vertindex) -{ - Q_assert(s_builtPhongCaches); - - // handle degenerate faces - const auto it = vertex_tangents.find(f); - if (it == vertex_tangents.end()) { - return {}; - } - const auto &face_tangents_vec = it->second; - return face_tangents_vec.at(vertindex); -} - -/* access the final phong-shaded vertex tangent */ -static const qvec3f GetSurfaceVertexBitangent(const mbsp_t *bsp, const mface_t *f, const int vertindex) -{ - Q_assert(s_builtPhongCaches); - - // handle degenerate faces - const auto it = vertex_bitangents.find(f); - if (it == vertex_bitangents.end()) { - return {}; - } - const auto &face_bitangents_vec = it->second; - return face_bitangents_vec.at(vertindex); -} - static bool FacesOnSamePlane(const std::vector &faces) { if (faces.empty()) { @@ -439,39 +412,20 @@ static edgeToFaceMap_t MakeEdgeToFaceMap(const mbsp_t *bsp) return result; } -static vector Face_VertexNormals(const mbsp_t *bsp, const mface_t *face) +static vector Face_VertexNormals(const mbsp_t *bsp, const mface_t *face) { - vector normals; + vector normals; for (int i = 0; i < face->numedges; i++) { - normals.push_back(GetSurfaceVertexNormal(bsp, face, i)); + normals.emplace_back(GetSurfaceVertexNormal(bsp, face, i)); } return normals; } -static vector Face_VertexTangents(const mbsp_t *bsp, const mface_t *face) -{ - vector tangents; - for (int i = 0; i < face->numedges; i++) { - tangents.push_back(GetSurfaceVertexTangent(bsp, face, i)); - } - return tangents; -} - -static vector Face_VertexBitangents(const mbsp_t *bsp, const mface_t *face) -{ - vector tangents; - for (int i = 0; i < face->numedges; i++) { - tangents.push_back(GetSurfaceVertexBitangent(bsp, face, i)); - } - return tangents; -} - static vector MakeFaceCache(const mbsp_t *bsp) { vector result; for (auto &face : bsp->dfaces) { - result.emplace_back(bsp, &face, Face_VertexNormals(bsp, &face), Face_VertexTangents(bsp, &face), - Face_VertexBitangents(bsp, &face)); + result.emplace_back(bsp, &face, Face_VertexNormals(bsp, &face)); } return result; } @@ -639,7 +593,7 @@ void CalculateVertexNormals(const mbsp_t *bsp) auto uv2 = uvs(texinfo->vecs, p2, miptex->width, miptex->height); auto uv3 = uvs(texinfo->vecs, p3, miptex->width, miptex->height); - auto tangents = compute_tangents({p1, p2, p3}, {uv1, uv2, uv3}, f_norm); + auto tangents = compute_tangents({p1, p2, p3}, {uv1, uv2, uv3}); // gather up f and neighboursToSmooth std::vector fPlusNeighbours; @@ -647,7 +601,7 @@ void CalculateVertexNormals(const mbsp_t *bsp) std::copy(neighboursToSmooth.begin(), neighboursToSmooth.end(), std::back_inserter(fPlusNeighbours)); // global vertex index -> smoothed normal - std::map smoothedNormals, smoothedTangents, smoothedBitangents; + std::map smoothedNormals; // walk fPlusNeighbours for (auto f2 : fPlusNeighbours) { @@ -659,7 +613,7 @@ void CalculateVertexNormals(const mbsp_t *bsp) v2 = Face_VertexAtIndex(bsp, f2, 1); for (int j = 2; j < f2->numedges; j++) { v3 = Face_VertexAtIndex(bsp, f2, j); - AddTriangleNormals(smoothedNormals, smoothedTangents, smoothedBitangents, + AddTriangleNormals(smoothedNormals, BSP_GetTexinfo(bsp, f2->texinfo), Face_RgbaMiptex(bsp, f2), f2_norm, bsp, v1, v2, v3); v2 = v3; } @@ -668,8 +622,8 @@ void CalculateVertexNormals(const mbsp_t *bsp) // normalize vertex normals (NOTE: updates smoothedNormals map) for (auto &pair : smoothedNormals) { const int vertIndex = pair.first; - const qvec3f vertNormal = pair.second; - if (0 == qv::length(vertNormal)) { + face_normal_t &vertNormal = pair.second; + if (0 == qv::length(vertNormal.normal)) { // this happens when there are colinear vertices, which give zero-area triangles, // so there is no contribution to the normal of the triangle in the middle of the // line. Not really an error, just set it to use the face normal. @@ -680,58 +634,30 @@ void CalculateVertexNormals(const mbsp_t *bsp) bsp->dvertexes[vertIndex].point[1], bsp->dvertexes[vertIndex].point[2]); #endif - pair.second = f_norm; + vertNormal = { f_norm, std::get<0>(tangents), std::get<1>(tangents) }; } else { - pair.second = qv::normalize(vertNormal); + vertNormal = { qv::normalize(vertNormal.normal), qv::normalize(vertNormal.tangent), qv::normalize(vertNormal.bitangent) }; } - } - - // normalize vertex tangents (NOTE: updates smoothedTangents map) - for (auto &pair : smoothedTangents) { - const int vertIndex = pair.first; - const qvec3f vertTangent = pair.second; - if (0 == qv::length(vertTangent)) { - // this happens when there are colinear vertices, which give zero-area triangles, - // so there is no contribution to the normal of the triangle in the middle of the - // line. Not really an error, just set it to use the face normal. -#if 0 - LogPrint("Failed to calculate normal for vertex {} at ({} {} {})\n", - vertIndex, - bsp->dvertexes[vertIndex].point[0], - bsp->dvertexes[vertIndex].point[1], - bsp->dvertexes[vertIndex].point[2]); -#endif - pair.second = std::get<0>(tangents); - } else { - pair.second = qv::normalize(vertTangent); + + // FIXME: why + if (std::isnan(vertNormal.tangent[0])) { + vertNormal.tangent = std::get<0>(tangents); + if (std::isnan(vertNormal.tangent[0])) { + vertNormal.tangent = { 0, 0, 0 }; + } } - } - - // normalize vertex tangents (NOTE: updates smoothedTangents map) - for (auto &pair : smoothedBitangents) { - const int vertIndex = pair.first; - const qvec3f vertBitangent = pair.second; - if (0 == qv::length(vertBitangent)) { - // this happens when there are colinear vertices, which give zero-area triangles, - // so there is no contribution to the normal of the triangle in the middle of the - // line. Not really an error, just set it to use the face normal. -#if 0 - LogPrint("Failed to calculate normal for vertex {} at ({} {} {})\n", - vertIndex, - bsp->dvertexes[vertIndex].point[0], - bsp->dvertexes[vertIndex].point[1], - bsp->dvertexes[vertIndex].point[2]); -#endif - pair.second = std::get<1>(tangents); - } else { - pair.second = qv::normalize(vertBitangent); + if (std::isnan(vertNormal.bitangent[0])) { + vertNormal.bitangent = std::get<1>(tangents); + if (std::isnan(vertNormal.bitangent[0])) { + vertNormal.bitangent = { 0, 0, 0 }; + } } } // sanity check if (!neighboursToSmooth.size()) { for (auto vertIndexNormalPair : smoothedNormals) { - Q_assert(qv::epsilonEqual(vertIndexNormalPair.second, f_norm, (float)EQUAL_EPSILON)); + Q_assert(qv::epsilonEqual(vertIndexNormalPair.second.normal, f_norm, (float)EQUAL_EPSILON)); } } @@ -739,12 +665,8 @@ void CalculateVertexNormals(const mbsp_t *bsp) for (int j = 0; j < f.numedges; j++) { int v = Face_VertexAtIndex(bsp, &f, j); Q_assert(smoothedNormals.find(v) != smoothedNormals.end()); - Q_assert(smoothedTangents.find(v) != smoothedTangents.end()); - Q_assert(smoothedBitangents.find(v) != smoothedBitangents.end()); vertex_normals[&f].push_back(smoothedNormals[v]); - vertex_tangents[&f].push_back(smoothedTangents[v]); - vertex_bitangents[&f].push_back(smoothedBitangents[v]); } } diff --git a/qbsp/qbsp.cc b/qbsp/qbsp.cc index 6ca9933f..dcd1bedb 100644 --- a/qbsp/qbsp.cc +++ b/qbsp/qbsp.cc @@ -273,7 +273,7 @@ static void ExportBrushList(const mapentity_t *entity, node_t *node, uint32_t &b for (auto &plane : bevels) { map.bsp.dbrushsides.push_back( - {(uint32_t)std::get<0>(plane), (int32_t)map.mtexinfos[b->faces->texinfo].outputnum.value_or(-1)}); + {(uint32_t)std::get<0>(plane), (int32_t)ExportMapTexinfo(b->faces->texinfo)}); brush.numsides++; brush_state.total_brush_sides++; }