summaryrefslogtreecommitdiff
path: root/game
diff options
context:
space:
mode:
Diffstat (limited to 'game')
-rw-r--r--game/geoData.cpp98
-rw-r--r--game/geoData.h1
-rw-r--r--game/geoDataMesh.cpp24
-rw-r--r--game/geoDataMesh.h31
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
{