diff options
Diffstat (limited to 'game')
-rw-r--r-- | game/geoData.cpp | 98 | ||||
-rw-r--r-- | game/geoData.h | 1 | ||||
-rw-r--r-- | game/geoDataMesh.cpp | 24 | ||||
-rw-r--r-- | game/geoDataMesh.h | 31 |
4 files changed, 90 insertions, 64 deletions
diff --git a/game/geoData.cpp b/game/geoData.cpp index e035a3c..dd7a3f8 100644 --- a/game/geoData.cpp +++ b/game/geoData.cpp @@ -291,6 +291,37 @@ GeoData::updateVertexNormal(VertexHandle vertex) set_normal(vertex, glm::normalize(n)); } +OpenMesh::VertexHandle +GeoData::setPoint(GlobalPosition3D tsPoint, const SetHeightsOpts & opts) +{ + const auto face = findPoint(tsPoint); + const auto distFromTsPoint = vertexDistanceFunction<2>(tsPoint); + // Check vertices + if (const auto nearest + = std::ranges::min(std::views::iota(fv_begin(face), fv_end(face)) | std::views::transform(distFromTsPoint), + {}, &std::pair<VertexHandle, float>::second); + nearest.second < opts.nearNodeTolerance) { + point(nearest.first).z = tsPoint.z; + return nearest.first; + } + // Check edges + if (const auto nearest + = std::ranges::min(std::views::iota(fh_begin(face), fh_end(face)) | std::views::transform(distFromTsPoint), + {}, &std::pair<HalfedgeHandle, float>::second); + nearest.second < opts.nearNodeTolerance) { + const auto from = point(from_vertex_handle(nearest.first)).xy(); + const auto to = point(to_vertex_handle(nearest.first)).xy(); + const auto v = vector_normal(from - to); + const auto inter = linesIntersectAt(from, to, tsPoint.xy(), tsPoint.xy() + v); + if (!inter) { + throw std::runtime_error("Perpendicular lines do not cross"); + } + return split_copy(edge_handle(nearest.first), *inter || tsPoint.z); + } + // Nothing close, split face + return split_copy(face, tsPoint); +}; + std::vector<GeoData::FaceHandle> GeoData::setHeights(const std::span<const GlobalPosition3D> triangleStrip, const SetHeightsOpts & opts) { @@ -301,57 +332,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 vertexDistFrom = [this](GlobalPosition2D p) { - return [p, this](const VertexHandle v) { - return std::make_pair(v, ::distance(p, this->point(v).xy())); - }; - }; - const auto vertexDistFromE = [this](GlobalPosition2D p) { - return [p, this](const HalfedgeHandle e) { - const auto fromPoint = point(from_vertex_handle(e)).xy(); - const auto toPoint = point(to_vertex_handle(e)).xy(); - return std::make_pair(e, Triangle<2> {fromPoint, toPoint, p}.height()); - }; - }; - 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())); }; - auto newVertexOnFace = [this, &vertexDistFrom, &opts, &vertexDistFromE](GlobalPosition3D tsPoint) { - const auto face = findPoint(tsPoint); - // Check vertices - if (const auto nearest = std::ranges::min( - std::views::iota(fv_begin(face), fv_end(face)) | std::views::transform(vertexDistFrom(tsPoint)), {}, - &std::pair<VertexHandle, float>::second); - nearest.second < opts.nearNodeTolerance) { - point(nearest.first).z = tsPoint.z; - return nearest.first; - } - // Check edges - if (const auto nearest = std::ranges::min( - std::views::iota(fh_begin(face), fh_end(face)) | std::views::transform(vertexDistFromE(tsPoint)), - {}, &std::pair<HalfedgeHandle, float>::second); - nearest.second < opts.nearNodeTolerance) { - const auto from = point(from_vertex_handle(nearest.first)).xy(); - const auto to = point(to_vertex_handle(nearest.first)).xy(); - const auto v = vector_normal(from - to); - const auto inter = linesIntersectAt(from, to, tsPoint.xy(), tsPoint.xy() + v); - if (!inter) { - throw std::runtime_error("Perpendicular lines do not cross"); - } - return split_copy(edge_handle(nearest.first), *inter || tsPoint.z); - } - // Nothing close, split face - return split_copy(face, tsPoint); - }; - // New vertices for each vertex in triangleStrip std::vector<VertexHandle> newVerts; newVerts.reserve(triangleStrip.size()); - std::transform(triangleStrip.begin(), triangleStrip.end(), std::back_inserter(newVerts), newVertexOnFace); + std::ranges::transform(triangleStrip, std::back_inserter(newVerts), [this, &opts](auto v) { + return setPoint(v, opts); + }); std::ranges::for_each(newVerts, addVertexForNormalUpdate); // Create temporary triangles from triangleStrip @@ -371,31 +363,11 @@ GeoData::setHeights(const std::span<const GlobalPosition3D> triangleStrip, const } return nullptr; }; - const auto canFlip = [this](const HalfedgeHandle edge) { - const auto opposite = opposite_halfedge_handle(edge); - const auto pointA = point(to_vertex_handle(edge)); - const auto pointB = point(to_vertex_handle(opposite)); - const auto pointC = point(to_vertex_handle(next_halfedge_handle(edge))); - const auto pointD = point(to_vertex_handle(next_halfedge_handle(opposite))); - - return Triangle<2> {pointC, pointB, pointD}.isUp() && Triangle<2> {pointA, pointC, pointD}.isUp(); - }; - const auto shouldFlip = [this, &canFlip](const HalfedgeHandle next, - const GlobalPosition2D startPoint) -> std::optional<EdgeHandle> { - if (const auto nextEdge = edge_handle(next); is_flip_ok(nextEdge) && canFlip(next)) { - const auto opposite_point - = point(to_vertex_handle(next_halfedge_handle(opposite_halfedge_handle(next)))).xy(); - if (distance<2>(startPoint, opposite_point) < length<2>(next)) { - return nextEdge; - } - } - return std::nullopt; - }; sanityCheck(); // Cut along each edge of triangleStrip AB, AC, BC, BD, CD, CE etc std::map<VertexHandle, const Triangle<3> *> boundaryTriangles; - auto doBoundaryPart = [this, &boundaryTriangles, &opts, &addVertexForNormalUpdate, &shouldFlip]( + auto doBoundaryPart = [this, &boundaryTriangles, &opts, &addVertexForNormalUpdate]( VertexHandle start, VertexHandle end, const Triangle<3> & triangle) { boundaryTriangles.emplace(start, &triangle); const auto endPoint = point(end); diff --git a/game/geoData.h b/game/geoData.h index 3d5ea5d..1a93d03 100644 --- a/game/geoData.h +++ b/game/geoData.h @@ -73,6 +73,7 @@ public: } protected: + [[nodiscard]] VertexHandle setPoint(GlobalPosition3D point, const SetHeightsOpts &); void updateAllVertexNormals(); template<std::ranges::range R> void updateAllVertexNormals(const R &); void updateVertexNormal(VertexHandle); diff --git a/game/geoDataMesh.cpp b/game/geoDataMesh.cpp index aaa8c9c..687a025 100644 --- a/game/geoDataMesh.cpp +++ b/game/geoDataMesh.cpp @@ -116,3 +116,27 @@ GeoDataMesh::sanityCheck(const std::source_location & loc) const "{} upside down faces detected - checked from {}:{}", upSideDown, loc.function_name(), loc.line())); } } + +bool +GeoDataMesh::canFlip(const HalfedgeHandle edge) const +{ + const auto opposite = opposite_halfedge_handle(edge); + const auto pointA = point(to_vertex_handle(edge)); + const auto pointB = point(to_vertex_handle(opposite)); + const auto pointC = point(to_vertex_handle(next_halfedge_handle(edge))); + const auto pointD = point(to_vertex_handle(next_halfedge_handle(opposite))); + + return Triangle<2> {pointC, pointB, pointD}.isUp() && Triangle<2> {pointA, pointC, pointD}.isUp(); +}; + +std::optional<OpenMesh::EdgeHandle> +GeoDataMesh::shouldFlip(const HalfedgeHandle next, const GlobalPosition2D startPoint) const +{ + if (const auto nextEdge = edge_handle(next); is_flip_ok(nextEdge) && canFlip(next)) { + const auto oppositePoint = point(to_vertex_handle(next_halfedge_handle(opposite_halfedge_handle(next)))).xy(); + if (distance<2>(startPoint, oppositePoint) < length<2>(next)) { + return nextEdge; + } + } + return std::nullopt; +}; diff --git a/game/geoDataMesh.h b/game/geoDataMesh.h index 00db67c..5d0bade 100644 --- a/game/geoDataMesh.h +++ b/game/geoDataMesh.h @@ -1,7 +1,6 @@ #pragma once #include "config/types.h" -#include "ray.h" #include "triangle.h" #include <OpenMesh/Core/Mesh/TriMesh_ArrayKernelT.hh> #include <source_location> @@ -61,6 +60,36 @@ protected: [[nodiscard]] HalfEdgePoints points(HalfEdgeVertices) const; template<glm::length_t D> + [[nodiscard]] auto + vertexDistanceFunction(GlobalPosition<D> point) const + { + struct DistanceCalculator { + [[nodiscard]] std::pair<VertexHandle, float> + operator()(VertexHandle compVertex) const + { + return std::make_pair( + compVertex, ::distance<D, GlobalDistance, glm::defaultp>(point, mesh->point(compVertex))); + } + + [[nodiscard]] + std::pair<HalfedgeHandle, float> + operator()(const HalfedgeHandle compHalfedge) const + { + const auto edgePoints = mesh->points(mesh->toVertexHandles(compHalfedge)); + return std::make_pair(compHalfedge, Triangle<2> {edgePoints.second, edgePoints.first, point}.height()); + }; + + const GeoDataMesh * mesh; + GlobalPosition<D> point; + }; + + return DistanceCalculator {this, point}; + } + + [[nodiscard]] bool canFlip(HalfedgeHandle edge) const; + [[nodiscard]] std::optional<EdgeHandle> shouldFlip(HalfedgeHandle next, GlobalPosition2D startPoint) const; + + template<glm::length_t D> [[nodiscard]] RelativeDistance length(HalfedgeHandle heh) const { |