summaryrefslogtreecommitdiff
path: root/game
diff options
context:
space:
mode:
Diffstat (limited to 'game')
-rw-r--r--game/geoData.cpp160
-rw-r--r--game/geoData.h74
-rw-r--r--game/geoDataMesh.cpp118
-rw-r--r--game/geoDataMesh.h83
4 files changed, 213 insertions, 222 deletions
diff --git a/game/geoData.cpp b/game/geoData.cpp
index 3b94564..a1d9762 100644
--- a/game/geoData.cpp
+++ b/game/geoData.cpp
@@ -110,74 +110,6 @@ GeoData::createFlat(GlobalPosition2D lower, GlobalPosition2D upper, GlobalDistan
return mesh;
}
-OpenMesh::FaceHandle
-GeoData::findPoint(GlobalPosition2D p) const
-{
- return findPoint(p, *faces_sbegin());
-}
-
-GeoData::PointFace::PointFace(const GlobalPosition2D p, const GeoData * mesh) :
- PointFace {p, mesh, *mesh->faces_sbegin()}
-{
-}
-
-GeoData::PointFace::PointFace(const GlobalPosition2D p, const GeoData * mesh, FaceHandle start) :
- PointFace {p, mesh->findPoint(p, start)}
-{
-}
-
-GeoData::FaceHandle
-GeoData::PointFace::face(const GeoData * mesh, FaceHandle start) const
-{
- if (_face.is_valid() && mesh->triangleContainsPoint(point, _face)) {
- return _face;
- }
- return (_face = mesh->findPoint(point, start));
-}
-
-GeoData::FaceHandle
-GeoData::PointFace::face(const GeoData * mesh) const
-{
- return face(mesh, *mesh->faces_sbegin());
-}
-
-namespace {
- constexpr GlobalPosition3D
- positionOnTriangle(const GlobalPosition2D point, const GeoData::Triangle<3> & t)
- {
- const CalcPosition3D a = t[1] - t[0], b = t[2] - t[0];
- const auto n = crossProduct(a, b);
- return {point, ((n.x * t[0].x) + (n.y * t[0].y) + (n.z * t[0].z) - (n.x * point.x) - (n.y * point.y)) / n.z};
- }
-
- static_assert(positionOnTriangle({7, -2}, {{1, 2, 3}, {1, 0, 1}, {-2, 1, 0}}) == GlobalPosition3D {7, -2, 3});
-}
-
-OpenMesh::FaceHandle
-GeoData::findPoint(GlobalPosition2D p, OpenMesh::FaceHandle f) const
-{
- while (f.is_valid() && !triangleContainsPoint(p, triangle<2>(f))) {
- for (auto next = cfh_iter(f); next.is_valid(); ++next) {
- f = opposite_face_handle(*next);
- if (f.is_valid()) {
- const auto e1 = point(to_vertex_handle(*next));
- const auto e2 = point(to_vertex_handle(opposite_halfedge_handle(*next)));
- if (pointLeftOfLine(p, e1, e2)) {
- break;
- }
- }
- f.reset();
- }
- }
- return f;
-}
-
-GlobalPosition3D
-GeoData::positionAt(const PointFace & p) const
-{
- return positionOnTriangle(p.point, triangle<3>(p.face(this)));
-}
-
[[nodiscard]] GeoData::IntersectionResult
GeoData::intersectRay(const Ray<GlobalPosition3D> & ray) const
{
@@ -230,11 +162,12 @@ GeoData::walkUntil(const PointFace & from, const GlobalPosition2D to, Tester<Wal
for (const auto next : fh_range(step.current)) {
step.current = opposite_face_handle(next);
if (step.current.is_valid() && step.current != step.previous) {
- const auto e1 = point(to_vertex_handle(next));
- const auto e2 = point(to_vertex_handle(opposite_halfedge_handle(next)));
- if (linesCrossLtR(from.point, to, e1, e2)) {
+ const auto nextPoints = points(toVertexHandles(next));
+ if (linesCrossLtR(from.point, to, nextPoints.second, nextPoints.first)) {
step.exitHalfedge = next;
- step.exitPosition = linesIntersectAt(from.point.xy(), to.xy(), e1.xy(), e2.xy()).value();
+ step.exitPosition
+ = linesIntersectAt(from.point.xy(), to.xy(), nextPoints.second.xy(), nextPoints.first.xy())
+ .value();
break;
}
}
@@ -337,46 +270,6 @@ GeoData::findEntry(const GlobalPosition2D from, const GlobalPosition2D to) const
return entry;
}
-bool
-GeoData::triangleContainsPoint(const GlobalPosition2D p, const Triangle<2> & t)
-{
- return pointLeftOfOrOnLine(p, t[0], t[1]) && pointLeftOfOrOnLine(p, t[1], t[2])
- && pointLeftOfOrOnLine(p, t[2], t[0]);
-}
-
-bool
-GeoData::triangleContainsPoint(const GlobalPosition2D p, FaceHandle face) const
-{
- return triangleContainsPoint(p, triangle<2>(face));
-}
-
-GeoData::HalfedgeHandle
-GeoData::findBoundaryStart() const
-{
- return *std::find_if(halfedges_sbegin(), halfedges_end(), [this](const auto heh) {
- return is_boundary(heh);
- });
-}
-
-[[nodiscard]] RelativePosition3D
-GeoData::difference(const HalfedgeHandle heh) const
-{
- return ::difference(point(to_vertex_handle(heh)), point(from_vertex_handle(heh)));
-}
-
-template<glm::length_t D>
-[[nodiscard]] RelativeDistance
-GeoData::length(const HalfedgeHandle heh) const
-{
- return ::distance<D, GlobalDistance, glm::defaultp>(point(to_vertex_handle(heh)), point(from_vertex_handle(heh)));
-}
-
-[[nodiscard]] GlobalPosition3D
-GeoData::centre(const HalfedgeHandle heh) const
-{
- return point(from_vertex_handle(heh)) + (difference(heh) / 2.F);
-}
-
void
GeoData::updateAllVertexNormals()
{
@@ -400,22 +293,6 @@ GeoData::updateVertexNormal(VertexHandle vertex)
set_normal(vertex, glm::normalize(n));
}
-bool
-GeoData::triangleOverlapsTriangle(const Triangle<2> & a, const Triangle<2> & b)
-{
- return triangleContainsPoint(a.x, b) || triangleContainsPoint(a.y, b) || triangleContainsPoint(a.z, b)
- || triangleContainsPoint(b.x, a) || triangleContainsPoint(b.y, a) || triangleContainsPoint(b.z, a)
- || linesCross(a.x, a.y, b.x, b.y) || linesCross(a.x, a.y, b.y, b.z) || linesCross(a.x, a.y, b.z, b.x)
- || linesCross(a.y, a.z, b.x, b.y) || linesCross(a.y, a.z, b.y, b.z) || linesCross(a.y, a.z, b.z, b.x)
- || linesCross(a.z, a.x, b.x, b.y) || linesCross(a.z, a.x, b.y, b.z) || linesCross(a.z, a.x, b.z, b.x);
-}
-
-bool
-GeoData::triangleContainsTriangle(const Triangle<2> & a, const Triangle<2> & b)
-{
- return triangleContainsPoint(a.x, b) && triangleContainsPoint(a.y, b) && triangleContainsPoint(a.z, b);
-}
-
std::vector<GeoData::FaceHandle>
GeoData::setHeights(const std::span<const GlobalPosition3D> triangleStrip, const SetHeightsOpts & opts)
{
@@ -489,7 +366,7 @@ GeoData::setHeights(const std::span<const GlobalPosition3D> triangleStrip, const
auto getTriangle = [&strip](const auto point) -> const Triangle<3> * {
if (const auto t = std::ranges::find_if(strip,
[point](const auto & triangle) {
- return triangleContainsPoint(point, triangle);
+ return triangle.containsPoint(point);
});
t != strip.end()) {
return &*t;
@@ -534,7 +411,7 @@ GeoData::setHeights(const std::span<const GlobalPosition3D> triangleStrip, const
/ distance(startPoint.xy(), endPoint.xy()))
< opts.nearNodeTolerance) {
start = adjVertex;
- point(start).z = positionOnTriangle(adjPoint, triangle).z;
+ point(start).z = triangle.positionOnPlane(adjPoint).z;
return true;
}
return false;
@@ -554,7 +431,7 @@ GeoData::setHeights(const std::span<const GlobalPosition3D> triangleStrip, const
flip(*nextEdge);
return true;
}
- start = split_copy(edge_handle(next), positionOnTriangle(*intersection, triangle));
+ start = split_copy(edge_handle(next), triangle.positionOnPlane(*intersection));
addVertexForNormalUpdate(start);
boundaryTriangles.emplace(start, &triangle);
return true;
@@ -610,7 +487,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;
+ toPoint.z = toTriangle->positionOnPlane(toPoint).z;
addVertexForNormalUpdate(toVertex);
todoOutHalfEdges(toVertex);
}
@@ -658,22 +535,3 @@ GeoData::getGeneration() const
{
return generation;
}
-
-void
-GeoData::sanityCheck(const std::source_location & loc) const
-{
- if (const auto upSideDown = std::ranges::count_if(faces(), [this](const auto face) {
- if (!triangle<2>(face).isUp()) {
-#ifndef NDEBUG
- for (const auto v : fv_range(face)) {
- CLOG(point(v));
- }
-#endif
- return true;
- }
- return false;
- }) > 0) {
- throw std::logic_error(std::format(
- "{} upside down faces detected - checked from {}:{}", upSideDown, loc.function_name(), loc.line()));
- }
-}
diff --git a/game/geoData.h b/game/geoData.h
index 03a2b3a..d486f22 100644
--- a/game/geoData.h
+++ b/game/geoData.h
@@ -1,28 +1,12 @@
#pragma once
#include "collections.h" // IWYU pragma: keep IterableCollection
-#include "config/types.h"
-#include "ray.h"
+#include "geoDataMesh.h"
#include "surface.h"
-#include "triangle.h"
-#include <OpenMesh/Core/Mesh/TriMesh_ArrayKernelT.hh>
#include <filesystem>
#include <glm/vec2.hpp>
-#include <optional>
-#include <source_location>
-#include <thirdparty/openmesh/glmcompat.h>
-#include <thirdparty/openmesh/helpers.h>
-
-struct GeoDataTraits : public OpenMesh::DefaultTraits {
- FaceAttributes(OpenMesh::Attributes::Status);
- EdgeAttributes(OpenMesh::Attributes::Status);
- VertexAttributes(OpenMesh::Attributes::Normal | OpenMesh::Attributes::Status);
- HalfedgeAttributes(OpenMesh::Attributes::Status);
- using Point = GlobalPosition3D;
- using Normal = Normal3D;
-};
-class GeoData : public OpenMesh::TriMesh_ArrayKernelT<GeoDataTraits> {
+class GeoData : public GeoDataMesh {
private:
const OpenMesh::Helpers::Property<const Surface *, OpenMesh::FPropHandleT> surface {this};
@@ -30,35 +14,6 @@ public:
static GeoData loadFromAsciiGrid(const std::filesystem::path &);
static GeoData createFlat(GlobalPosition2D lower, GlobalPosition2D upper, GlobalDistance h);
- struct PointFace {
- // NOLINTNEXTLINE(hicpp-explicit-conversions)
- PointFace(const GlobalPosition2D p) : point {p} { }
-
- PointFace(const GlobalPosition2D p, FaceHandle face) : point {p}, _face {face} { }
-
- PointFace(const GlobalPosition2D p, const GeoData *);
- PointFace(const GlobalPosition2D p, const GeoData *, FaceHandle start);
-
- const GlobalPosition2D point;
- [[nodiscard]] FaceHandle face(const GeoData *) const;
- [[nodiscard]] FaceHandle face(const GeoData *, FaceHandle start) const;
-
- [[nodiscard]] bool
- isLocated() const
- {
- return _face.is_valid();
- }
-
- private:
- mutable FaceHandle _face {};
- };
-
- template<glm::length_t Dim> using Triangle = ::Triangle<Dim, GlobalDistance>;
-
- [[nodiscard]] FaceHandle findPoint(GlobalPosition2D) const;
- [[nodiscard]] FaceHandle findPoint(GlobalPosition2D, FaceHandle start) const;
-
- [[nodiscard]] GlobalPosition3D positionAt(const PointFace &) const;
using IntersectionLocation = std::pair<GlobalPosition3D, FaceHandle>;
using IntersectionResult = std::optional<IntersectionLocation>;
[[nodiscard]] IntersectionResult intersectRay(const Ray<GlobalPosition3D> &) const;
@@ -89,7 +44,7 @@ public:
void boundaryWalkUntil(Tester<HalfedgeHandle>) const;
void boundaryWalkUntil(Tester<HalfedgeHandle>, HalfedgeHandle start) const;
- [[nodiscard]] HalfedgeHandle findEntry(const GlobalPosition2D from, const GlobalPosition2D to) const;
+ [[nodiscard]] HalfedgeHandle findEntry(GlobalPosition2D from, GlobalPosition2D to) const;
struct SetHeightsOpts {
static constexpr auto DEFAULT_NEAR_NODE_TOLERANACE = 500.F;
@@ -118,30 +73,7 @@ public:
return property(surface, handle);
}
- void sanityCheck(const std::source_location & = std::source_location::current()) const;
-
protected:
- template<glm::length_t Dim>
- [[nodiscard]] Triangle<Dim>
- triangle(FaceHandle face) const
- {
- Triangle<Dim> triangle;
- std::ranges::transform(fv_range(face), triangle.begin(), [this](auto vertex) {
- return point(vertex);
- });
- return triangle;
- }
-
- [[nodiscard]] static bool triangleContainsPoint(const GlobalPosition2D, const Triangle<2> &);
- [[nodiscard]] bool triangleContainsPoint(const GlobalPosition2D, FaceHandle) const;
- [[nodiscard]] static bool triangleOverlapsTriangle(const Triangle<2> &, const Triangle<2> &);
- [[nodiscard]] static bool triangleContainsTriangle(const Triangle<2> &, const Triangle<2> &);
- [[nodiscard]] HalfedgeHandle findBoundaryStart() const;
- [[nodiscard]] RelativePosition3D difference(const HalfedgeHandle) const;
-
- template<glm::length_t D> [[nodiscard]] RelativeDistance length(const HalfedgeHandle) const;
- [[nodiscard]] GlobalPosition3D centre(const HalfedgeHandle) const;
-
void updateAllVertexNormals();
template<std::ranges::range R> void updateAllVertexNormals(const R &);
void updateVertexNormal(VertexHandle);
diff --git a/game/geoDataMesh.cpp b/game/geoDataMesh.cpp
new file mode 100644
index 0000000..aaa8c9c
--- /dev/null
+++ b/game/geoDataMesh.cpp
@@ -0,0 +1,118 @@
+#include "geoDataMesh.h"
+#include <format>
+#ifndef NDEBUG
+# include <stream_support.h>
+#endif
+
+OpenMesh::FaceHandle
+GeoDataMesh::findPoint(GlobalPosition2D coord) const
+{
+ return findPoint(coord, *faces_sbegin());
+}
+
+GeoDataMesh::PointFace::PointFace(const GlobalPosition2D coord, const GeoDataMesh * mesh) :
+ PointFace {coord, mesh, *mesh->faces_sbegin()}
+{
+}
+
+GeoDataMesh::PointFace::PointFace(const GlobalPosition2D coord, const GeoDataMesh * mesh, FaceHandle start) :
+ PointFace {coord, mesh->findPoint(coord, start)}
+{
+}
+
+OpenMesh::FaceHandle
+GeoDataMesh::PointFace::face(const GeoDataMesh * mesh, FaceHandle start) const
+{
+ if (faceCache.is_valid() && mesh->faceContainsPoint(point, faceCache)) {
+ return faceCache;
+ }
+ return (faceCache = mesh->findPoint(point, start));
+}
+
+OpenMesh::FaceHandle
+GeoDataMesh::PointFace::face(const GeoDataMesh * mesh) const
+{
+ return face(mesh, *mesh->faces_sbegin());
+}
+
+GeoDataMesh::HalfEdgeVertices
+GeoDataMesh::toVertexHandles(HalfedgeHandle halfEdge) const
+{
+ return {from_vertex_handle(halfEdge), to_vertex_handle(halfEdge)};
+}
+
+GeoDataMesh::HalfEdgePoints
+GeoDataMesh::points(HalfEdgeVertices vertices) const
+{
+ return {point(vertices.first), point(vertices.second)};
+}
+
+OpenMesh::FaceHandle
+GeoDataMesh::findPoint(const GlobalPosition2D coord, OpenMesh::FaceHandle face) const
+{
+ while (face.is_valid() && !triangle<2>(face).containsPoint(coord)) {
+ for (auto next = cfh_iter(face); next.is_valid(); ++next) {
+ face = opposite_face_handle(*next);
+ if (face.is_valid()) {
+ const auto nextPoints = points(toVertexHandles(*next));
+ if (pointLeftOfLine(coord, nextPoints.second, nextPoints.first)) {
+ break;
+ }
+ }
+ face.reset();
+ }
+ }
+ return face;
+}
+
+GlobalPosition3D
+GeoDataMesh::positionAt(const PointFace & coord) const
+{
+ return triangle<3>(coord.face(this)).positionOnPlane(coord.point);
+}
+
+bool
+GeoDataMesh::faceContainsPoint(const GlobalPosition2D coord, FaceHandle face) const
+{
+ return triangle<2>(face).containsPoint(coord);
+}
+
+OpenMesh::HalfedgeHandle
+GeoDataMesh::findBoundaryStart() const
+{
+ return *std::find_if(halfedges_sbegin(), halfedges_end(), [this](const auto heh) {
+ return is_boundary(heh);
+ });
+}
+
+[[nodiscard]] RelativePosition3D
+GeoDataMesh::difference(const HalfedgeHandle heh) const
+{
+ return ::difference(point(to_vertex_handle(heh)), point(from_vertex_handle(heh)));
+}
+
+[[nodiscard]] GlobalPosition3D
+GeoDataMesh::centre(const HalfedgeHandle heh) const
+{
+ const auto hehPoints = points(toVertexHandles(heh));
+ return midpoint(hehPoints.first, hehPoints.second);
+}
+
+void
+GeoDataMesh::sanityCheck(const std::source_location & loc) const
+{
+ if (const auto upSideDown = std::ranges::count_if(faces(), [this](const auto face) {
+ if (!triangle<2>(face).isUp()) {
+#ifndef NDEBUG
+ for (const auto vertex : fv_range(face)) {
+ CLOG(point(vertex));
+ }
+#endif
+ return true;
+ }
+ return false;
+ }) > 0) {
+ throw std::logic_error(std::format(
+ "{} upside down faces detected - checked from {}:{}", upSideDown, loc.function_name(), loc.line()));
+ }
+}
diff --git a/game/geoDataMesh.h b/game/geoDataMesh.h
new file mode 100644
index 0000000..00db67c
--- /dev/null
+++ b/game/geoDataMesh.h
@@ -0,0 +1,83 @@
+#pragma once
+
+#include "config/types.h"
+#include "ray.h"
+#include "triangle.h"
+#include <OpenMesh/Core/Mesh/TriMesh_ArrayKernelT.hh>
+#include <source_location>
+#include <thirdparty/openmesh/glmcompat.h>
+#include <thirdparty/openmesh/helpers.h>
+
+struct GeoDataTraits : public OpenMesh::DefaultTraits {
+ FaceAttributes(OpenMesh::Attributes::Status);
+ EdgeAttributes(OpenMesh::Attributes::Status);
+ VertexAttributes(OpenMesh::Attributes::Normal | OpenMesh::Attributes::Status);
+ HalfedgeAttributes(OpenMesh::Attributes::Status);
+ using Point = GlobalPosition3D;
+ using Normal = Normal3D;
+};
+
+class GeoDataMesh : public OpenMesh::TriMesh_ArrayKernelT<GeoDataTraits> {
+public:
+ struct PointFace {
+ // NOLINTNEXTLINE(hicpp-explicit-conversions)
+ PointFace(GlobalPosition2D coord) : point {coord} { }
+
+ PointFace(GlobalPosition2D coord, FaceHandle face) : point {coord}, faceCache {face} { }
+
+ PointFace(GlobalPosition2D coord, const GeoDataMesh *);
+ PointFace(GlobalPosition2D coord, GeoDataMesh const *, FaceHandle start);
+
+ const GlobalPosition2D point;
+ [[nodiscard]] FaceHandle face(const GeoDataMesh *) const;
+ [[nodiscard]] FaceHandle face(const GeoDataMesh *, FaceHandle start) const;
+
+ [[nodiscard]] bool
+ isLocated() const
+ {
+ return faceCache.is_valid();
+ }
+
+ private:
+ mutable FaceHandle faceCache;
+ };
+
+ template<glm::length_t Dim> using Triangle = ::Triangle<Dim, GlobalDistance>;
+
+ [[nodiscard]] FaceHandle findPoint(GlobalPosition2D) const;
+ [[nodiscard]] FaceHandle findPoint(GlobalPosition2D, FaceHandle) const;
+
+ [[nodiscard]] GlobalPosition3D positionAt(const PointFace &) const;
+
+protected:
+ void sanityCheck(const std::source_location & = std::source_location::current()) const;
+
+ [[nodiscard]] bool faceContainsPoint(GlobalPosition2D, FaceHandle) const;
+ [[nodiscard]] HalfedgeHandle findBoundaryStart() const;
+ [[nodiscard]] RelativePosition3D difference(HalfedgeHandle) const;
+ using HalfEdgeVertices = std::pair<VertexHandle, VertexHandle>;
+ [[nodiscard]] HalfEdgeVertices toVertexHandles(HalfedgeHandle) const;
+ using HalfEdgePoints = std::pair<GlobalPosition3D, GlobalPosition3D>;
+ [[nodiscard]] HalfEdgePoints points(HalfEdgeVertices) const;
+
+ template<glm::length_t D>
+ [[nodiscard]] RelativeDistance
+ length(HalfedgeHandle heh) const
+ {
+ return ::distance<D, GlobalDistance, glm::defaultp>(
+ point(to_vertex_handle(heh)), point(from_vertex_handle(heh)));
+ }
+
+ [[nodiscard]] GlobalPosition3D centre(HalfedgeHandle) const;
+
+ template<glm::length_t Dim>
+ [[nodiscard]] Triangle<Dim>
+ triangle(FaceHandle face) const
+ {
+ Triangle<Dim> triangle;
+ std::ranges::transform(fv_range(face), triangle.begin(), [this](auto vertex) {
+ return point(vertex);
+ });
+ return triangle;
+ }
+};