diff options
-rw-r--r-- | game/geoData.cpp | 43 | ||||
-rw-r--r-- | game/geoData.h | 5 | ||||
-rw-r--r-- | test/test-geoData.cpp | 11 |
3 files changed, 37 insertions, 22 deletions
diff --git a/game/geoData.cpp b/game/geoData.cpp index cd0be29..f43e231 100644 --- a/game/geoData.cpp +++ b/game/geoData.cpp @@ -66,7 +66,7 @@ GeoData::loadFromAsciiGrid(const std::filesystem::path & input) }); } } - mesh.update_vertex_normals_only(); + mesh.updateAllVertexNormals(); return mesh; }; @@ -105,7 +105,7 @@ GeoData::createFlat(GlobalPosition2D lower, GlobalPosition2D upper, GlobalDistan } } - mesh.update_vertex_normals_only(); + mesh.updateAllVertexNormals(); return mesh; } @@ -377,23 +377,28 @@ GeoData::centre(const HalfedgeHandle heh) const } void -GeoData::update_vertex_normals_only() +GeoData::updateAllVertexNormals() { - update_vertex_normals_only(vertices_sbegin()); + updateAllVertexNormals(vertices()); } +template<std::ranges::range R> void -GeoData::update_vertex_normals_only(VertexIter start) +GeoData::updateAllVertexNormals(const R & range) { - std::for_each(start, vertices_end(), [this](const auto vh) { - if (normal(vh) == Normal3D {}) { - Normal3D n; - calc_vertex_normal_correct(vh, n); - this->set_normal(vh, glm::normalize(n)); - } + std::ranges::for_each(range, [this](const auto vertex) { + updateVertexNormal(vertex); }); } +void +GeoData::updateVertexNormal(VertexHandle vertex) +{ + Normal3D n; + calc_vertex_normal_correct(vertex, n); + set_normal(vertex, glm::normalize(n)); +} + bool GeoData::triangleOverlapsTriangle(const Triangle<2> & a, const Triangle<2> & b) { @@ -420,14 +425,18 @@ GeoData::setHeights(const std::span<const GlobalPosition3D> triangleStrip, const lowerExtent.z = std::min(upperExtent.z, stripMinMax.min.z); upperExtent.z = std::max(upperExtent.z, stripMinMax.max.z); - const auto initialVertexCount = static_cast<unsigned int>(n_vertices()); - const auto vertexDistFrom = [this](GlobalPosition2D p) { return [p, this](const VertexHandle v) { return std::make_pair(v, glm::length(difference(this->point(v).xy(), p))); }; }; + std::set<VertexHandle> newOrChangedVerts; + auto addVertexForNormalUpdate = [this, &newOrChangedVerts](const VertexHandle vertex) { + newOrChangedVerts.emplace(vertex); + std::ranges::copy(vv_range(vertex), std::inserter(newOrChangedVerts, newOrChangedVerts.end())); + }; + // New vertices for each vertex in triangleStrip std::vector<VertexHandle> newVerts; newVerts.reserve(newVerts.size()); @@ -443,6 +452,7 @@ GeoData::setHeights(const std::span<const GlobalPosition3D> triangleStrip, const } return split(face, tsPoint); }); + std::ranges::for_each(newVerts, addVertexForNormalUpdate); // Create temporary triangles from triangleStrip std::vector<Triangle<3>> strip; @@ -464,7 +474,7 @@ GeoData::setHeights(const std::span<const GlobalPosition3D> triangleStrip, const // Cut along each edge of triangleStrip AB, AC, BC, BD, CD, CE etc std::map<VertexHandle, const Triangle<3> *> boundaryTriangles; - auto doBoundaryPart = [this, &boundaryTriangles, &newVerts, &vertexDistFrom, &opts]( + auto doBoundaryPart = [this, &boundaryTriangles, &newVerts, &vertexDistFrom, &opts, &addVertexForNormalUpdate]( VertexHandle start, VertexHandle end, const Triangle<3> & triangle) { boundaryTriangles.emplace(start, &triangle); const auto endPoint = point(end); @@ -491,6 +501,7 @@ GeoData::setHeights(const std::span<const GlobalPosition3D> triangleStrip, const else { start = split(edge_handle(next), positionOnTriangle(*intersection, triangle)); } + addVertexForNormalUpdate(start); boundaryTriangles.emplace(start, &triangle); return true; } @@ -530,6 +541,7 @@ GeoData::setHeights(const std::span<const GlobalPosition3D> triangleStrip, const } if (toTriangle) { // point within the new strip, adjust vertically by triangle toPoint.z = positionOnTriangle(toPoint, *toTriangle).z; + addVertexForNormalUpdate(toVertex); todoOutHalfEdges(toVertex); } else if (!toTriangle) { // point without the new strip, adjust vertically by limit @@ -537,6 +549,7 @@ GeoData::setHeights(const std::span<const GlobalPosition3D> triangleStrip, const const auto newHeight = std::clamp(toPoint.z, fromPoint.z - maxOffset, fromPoint.z + maxOffset); if (newHeight != toPoint.z) { toPoint.z = newHeight; + addVertexForNormalUpdate(toVertex); std::copy_if(voh_begin(toVertex), voh_end(toVertex), std::inserter(todo, todo.end()), [this, &boundaryTriangles](const auto & heh) { return !boundaryTriangles.contains(to_vertex_handle(heh)); @@ -559,5 +572,5 @@ GeoData::setHeights(const std::span<const GlobalPosition3D> triangleStrip, const }; surfaceStripWalk(surfaceStripWalk, findPoint(strip.front().centroid())); - update_vertex_normals_only(VertexIter {*this, vertex_handle(initialVertexCount), true}); + updateAllVertexNormals(newOrChangedVerts); } diff --git a/game/geoData.h b/game/geoData.h index c28dd14..71ec85f 100644 --- a/game/geoData.h +++ b/game/geoData.h @@ -200,8 +200,9 @@ protected: [[nodiscard]] RelativeDistance length(const HalfedgeHandle) const; [[nodiscard]] GlobalPosition3D centre(const HalfedgeHandle) const; - void update_vertex_normals_only(); - void update_vertex_normals_only(VertexIter start); + void updateAllVertexNormals(); + template<std::ranges::range R> void updateAllVertexNormals(const R &); + void updateVertexNormal(VertexHandle); private: GlobalPosition3D lowerExtent {}, upperExtent {}; diff --git a/test/test-geoData.cpp b/test/test-geoData.cpp index 17e1741..2f3d215 100644 --- a/test/test-geoData.cpp +++ b/test/test-geoData.cpp @@ -31,11 +31,9 @@ BOOST_AUTO_TEST_CASE(loadSuccess) BOOST_AUTO_TEST_CASE(normalsAllPointUp) { - BOOST_CHECK_EQUAL(std::count_if(vertices_begin(), vertices_end(), - [this](auto && vh) { - return normal(vh).z > 0; - }), - n_vertices()); + BOOST_CHECK(std::ranges::all_of(vertices(), [this](auto && vertex) { + return normal(vertex).z > 0; + })); } BOOST_AUTO_TEST_CASE(trianglesContainsPoints) @@ -233,6 +231,9 @@ BOOST_DATA_TEST_CASE(deform, loadFixtureJson<DeformTerrainData>("geoData/deform/ surface.colorBias = RGB {0, 0, 1}; auto gd = std::make_shared<GeoData>(GeoData::createFlat({0, 0}, {1000000, 1000000}, 100)); BOOST_CHECK_NO_THROW(gd->setHeights(points, {.surface = surface})); + BOOST_CHECK(std::ranges::all_of(gd->vertices(), [&gd](auto && vertex) { + return gd->normal(vertex).z > 0; + })); ApplicationBase ab; TestMainWindow tmw; |