From ca276ca5471a4e7a137f68a81feb150282eae62f Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Wed, 7 Feb 2024 23:50:50 +0000 Subject: Add stripiter A generic iterator wrapper returning a tuple of 3 references to the original values, as processed in the fashion of an OpenGL triangle strip. --- lib/collections.h | 60 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ test/test-lib.cpp | 20 +++++++++++++++++++ 2 files changed, 80 insertions(+) diff --git a/lib/collections.h b/lib/collections.h index 943b986..6cee10c 100644 --- a/lib/collections.h +++ b/lib/collections.h @@ -2,6 +2,7 @@ #include #include +#include #include #include #include @@ -160,3 +161,62 @@ template struct pair_range { }; template pair_range(std::pair) -> pair_range; + +template struct stripiter { + [[nodiscard]] constexpr bool + operator!=(const stripiter & other) const + { + return current != other.current; + } + + [[nodiscard]] constexpr bool + operator==(const stripiter & other) const + { + return current == other.current; + } + + constexpr stripiter & + operator++() + { + ++current; + off = 1 - off; + return *this; + } + + constexpr stripiter & + operator--() + { + --current; + off = 1 - off; + return *this; + } + + constexpr auto + operator-(const stripiter & other) const + { + return current - other.current; + } + + constexpr auto + operator*() const + { + return std::tie(*(current - (2 - off)), *(current - off - 1), *current); + } + + iter current; + uint8_t off {}; +}; + +template struct std::iterator_traits> : std::iterator_traits { }; + +constexpr auto +strip_begin(IterableCollection auto & cont) +{ + return stripiter {cont.begin() + 2}; +} + +constexpr auto +strip_end(IterableCollection auto & cont) +{ + return stripiter {cont.end()}; +} diff --git a/test/test-lib.cpp b/test/test-lib.cpp index 58b769a..5f0b5e5 100644 --- a/test/test-lib.cpp +++ b/test/test-lib.cpp @@ -4,6 +4,7 @@ #include #include +#include #include #include #include @@ -49,3 +50,22 @@ BOOST_AUTO_TEST_CASE(generate_move_and_delete) } BOOST_CHECK(active.empty()); } + +constexpr std::array TRIANGLE_STRIP_IN {0, 1, 2, 3, 4, 5}; +static_assert(std::distance(strip_begin(TRIANGLE_STRIP_IN), strip_end(TRIANGLE_STRIP_IN)) == 4); + +BOOST_AUTO_TEST_CASE(triangle_strip_iter) +{ + constexpr std::array TRIANGLE_STRIP_EXPECTED {0, 1, 2, 2, 1, 3, 2, 3, 4, 4, 3, 5}; + + std::vector out; + std::for_each(strip_begin(TRIANGLE_STRIP_IN), strip_end(TRIANGLE_STRIP_IN), [&out](const auto & t) { + const auto [a, b, c] = t; + out.push_back(a); + out.push_back(b); + out.push_back(c); + }); + BOOST_REQUIRE_EQUAL(out.size(), (TRIANGLE_STRIP_IN.size() - 2) * 3); + BOOST_CHECK_EQUAL_COLLECTIONS( + out.begin(), out.end(), TRIANGLE_STRIP_EXPECTED.begin(), TRIANGLE_STRIP_EXPECTED.end()); +} -- cgit v1.2.3 From fa48542d7387ba376b56dc4f60f90fccbd037f92 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Mon, 12 Feb 2024 20:28:50 +0000 Subject: Cast relative position accordingly in geometric plane --- lib/geometricPlane.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/geometricPlane.h b/lib/geometricPlane.h index 3f95d3c..3b1a0e4 100644 --- a/lib/geometricPlane.h +++ b/lib/geometricPlane.h @@ -27,7 +27,7 @@ public: [[nodiscard]] inline PlaneRelation getRelation(PositionType point) const { - const auto d = glm::dot(normal, point - origin); + const auto d = glm::dot(normal, RelativePosition3D(point - origin)); return d < 0.F ? PlaneRelation::Below : d > 0.F ? PlaneRelation::Above : PlaneRelation::On; } -- cgit v1.2.3 From 40f5a59dba7d5061821e143cebcfa30f6faa9464 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Mon, 12 Feb 2024 20:32:01 +0000 Subject: Add materializeRange override for naked iterator pair --- lib/collections.h | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/lib/collections.h b/lib/collections.h index 6cee10c..dd603be 100644 --- a/lib/collections.h +++ b/lib/collections.h @@ -130,18 +130,25 @@ vectorOfN(std::integral auto N, T start = {}, T step = 1) return v; } +template typename Rtn = std::vector, typename In> +[[nodiscard]] auto +materializeRange(const In begin, const In end) +{ + return Rtn(begin, end); +} + template typename Rtn = std::vector, IterableCollection In> [[nodiscard]] auto -materializeRange(In && in) +materializeRange(const In & in) { - return Rtn(in.begin(), in.end()); + return materializeRange(in.begin(), in.end()); } template typename Rtn = std::vector, typename In> [[nodiscard]] auto materializeRange(const std::pair & in) { - return Rtn(in.first, in.second); + return materializeRange(in.first, in.second); } template struct pair_range { -- cgit v1.2.3 From e0c5affab138cdc35074d66d641dae2dba1ab3f1 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Mon, 12 Feb 2024 20:40:20 +0000 Subject: Create flat GeoData as 10m squares instead of one big square --- game/geoData.cpp | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/game/geoData.cpp b/game/geoData.cpp index b30a35b..49cf892 100644 --- a/game/geoData.cpp +++ b/game/geoData.cpp @@ -62,6 +62,8 @@ GeoData::loadFromAsciiGrid(const std::filesystem::path & input) return mesh; }; +template constexpr static T GRID_SIZE = 10'000; + GeoData GeoData::createFlat(GlobalPosition2D lower, GlobalPosition2D upper, GlobalDistance h) { @@ -70,11 +72,29 @@ GeoData::createFlat(GlobalPosition2D lower, GlobalPosition2D upper, GlobalDistan mesh.lowerExtent = {lower, h}; mesh.upperExtent = {upper, h}; - const auto ll = mesh.add_vertex({lower.x, lower.y, h}), lu = mesh.add_vertex({lower.x, upper.y, h}), - ul = mesh.add_vertex({upper.x, lower.y, h}), uu = mesh.add_vertex({upper.x, upper.y, h}); + std::vector vertices; + for (GlobalDistance row = lower.x; row < upper.x; row += GRID_SIZE) { + for (GlobalDistance col = lower.y; col < upper.y; col += GRID_SIZE) { + vertices.push_back(mesh.add_vertex({col, row, h})); + } + } - mesh.add_face(ll, uu, lu); - mesh.add_face(ll, ul, uu); + const auto nrows = static_cast(std::ceil(float(upper.x - lower.x) / GRID_SIZE)); + const auto ncols = static_cast(std::ceil(float(upper.y - lower.y) / GRID_SIZE)); + for (size_t row = 1; row < nrows; ++row) { + for (size_t col = 1; col < ncols; ++col) { + mesh.add_face({ + vertices[ncols * (row - 1) + (col - 1)], + vertices[ncols * (row - 0) + (col - 0)], + vertices[ncols * (row - 0) + (col - 1)], + }); + mesh.add_face({ + vertices[ncols * (row - 1) + (col - 1)], + vertices[ncols * (row - 1) + (col - 0)], + vertices[ncols * (row - 0) + (col - 0)], + }); + } + } mesh.update_vertex_normals_only(); -- cgit v1.2.3 From 68a23b27925142fed3b1e53b2f17ad2c90878e21 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Mon, 12 Feb 2024 23:25:00 +0000 Subject: Add helpers for testing for triangle overlap/containedness --- game/geoData.cpp | 16 ++++++++++++++++ game/geoData.h | 2 ++ 2 files changed, 18 insertions(+) diff --git a/game/geoData.cpp b/game/geoData.cpp index 49cf892..ae43f3f 100644 --- a/game/geoData.cpp +++ b/game/geoData.cpp @@ -359,3 +359,19 @@ GeoData::update_vertex_normals_only() this->set_normal(vh, 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); +} diff --git a/game/geoData.h b/game/geoData.h index e234bfe..2fd3aa5 100644 --- a/game/geoData.h +++ b/game/geoData.h @@ -103,6 +103,8 @@ protected: [[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; void update_vertex_normals_only(); -- cgit v1.2.3 From 849f4aa735352704995ffb51bf23dadf795bb119 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Tue, 13 Feb 2024 01:36:42 +0000 Subject: Include face handle in intersectRay result --- game/geoData.cpp | 8 ++++---- game/geoData.h | 6 ++++-- test/test-geoData.cpp | 2 +- ui/builders/freeExtend.cpp | 6 +++--- ui/builders/straight.cpp | 6 +++--- 5 files changed, 15 insertions(+), 13 deletions(-) diff --git a/game/geoData.cpp b/game/geoData.cpp index ae43f3f..0dbc0ac 100644 --- a/game/geoData.cpp +++ b/game/geoData.cpp @@ -213,16 +213,16 @@ GeoData::positionAt(const PointFace & p) const return positionOnTriangle(p.point, triangle<3>(p.face(this))); } -[[nodiscard]] std::optional +[[nodiscard]] GeoData::IntersectionResult GeoData::intersectRay(const Ray & ray) const { return intersectRay(ray, findPoint(ray.start)); } -[[nodiscard]] std::optional +[[nodiscard]] GeoData::IntersectionResult GeoData::intersectRay(const Ray & ray, FaceHandle face) const { - std::optional out; + GeoData::IntersectionResult out; walkUntil(PointFace {ray.start, face}, ray.start.xy() + GlobalPosition2D(ray.direction.xy() * RelativePosition2D(upperExtent.xy() - lowerExtent.xy())), @@ -231,7 +231,7 @@ GeoData::intersectRay(const Ray & ray, FaceHandle face) const RelativeDistance dist {}; const auto t = triangle<3>(face); if (ray.intersectTriangle(t.x, t.y, t.z, bari, dist)) { - out = t * bari; + out.emplace(t * bari, face); return true; } return false; diff --git a/game/geoData.h b/game/geoData.h index 2fd3aa5..f599552 100644 --- a/game/geoData.h +++ b/game/geoData.h @@ -74,8 +74,10 @@ public: [[nodiscard]] FaceHandle findPoint(GlobalPosition2D, FaceHandle start) const; [[nodiscard]] GlobalPosition3D positionAt(const PointFace &) const; - [[nodiscard]] std::optional intersectRay(const Ray &) const; - [[nodiscard]] std::optional intersectRay(const Ray &, FaceHandle start) const; + using IntersectionLocation = std::pair; + using IntersectionResult = std::optional; + [[nodiscard]] IntersectionResult intersectRay(const Ray &) const; + [[nodiscard]] IntersectionResult intersectRay(const Ray &, FaceHandle start) const; void walk(const PointFace & from, const GlobalPosition2D to, const std::function & op) const; void walkUntil(const PointFace & from, const GlobalPosition2D to, const std::function & op) const; diff --git a/test/test-geoData.cpp b/test/test-geoData.cpp index efe845c..f8437c3 100644 --- a/test/test-geoData.cpp +++ b/test/test-geoData.cpp @@ -114,7 +114,7 @@ BOOST_DATA_TEST_CASE(findRayIntersect, }), p, d, i) { - BOOST_CHECK_EQUAL(fixedTerrtain.intersectRay({p, d}).value(), i); + BOOST_CHECK_EQUAL(fixedTerrtain.intersectRay({p, d})->first, i); } BOOST_AUTO_TEST_CASE(boundaryWalk) diff --git a/ui/builders/freeExtend.cpp b/ui/builders/freeExtend.cpp index 47356c3..db127e6 100644 --- a/ui/builders/freeExtend.cpp +++ b/ui/builders/freeExtend.cpp @@ -19,7 +19,7 @@ BuilderFreeExtend::move( candidateLinks.objects = network->candidateJoins(*p1, p->pos); } else if (const auto p = geoData->intersectRay(ray)) { - candidateLinks.objects = network->candidateExtend(*p1, *p); + candidateLinks.objects = network->candidateExtend(*p1, p->first); } else { candidateLinks.removeAll(); @@ -42,8 +42,8 @@ BuilderFreeExtend::click( p1 = p->pos; } else if (const auto p = geoData->intersectRay(ray)) { - network->addExtend(*p1, *p); - p1 = *p; + network->addExtend(*p1, p->first); + p1 = p->first; } } else { diff --git a/ui/builders/straight.cpp b/ui/builders/straight.cpp index 0c4a3e2..43f5ec8 100644 --- a/ui/builders/straight.cpp +++ b/ui/builders/straight.cpp @@ -16,7 +16,7 @@ BuilderStraight::move( { if (p1) { if (const auto p = geoData->intersectRay(ray)) { - candidateLinks.objects = network->candidateStraight(*p1, *p); + candidateLinks.objects = network->candidateStraight(*p1, p->first); } else { candidateLinks.removeAll(); @@ -32,12 +32,12 @@ BuilderStraight::click( case SDL_BUTTON_LEFT: if (const auto p = geoData->intersectRay(ray)) { if (p1) { - create(network, *p1, *p); + create(network, *p1, p->first); candidateLinks.removeAll(); p1.reset(); } else { - p1 = p; + p1 = p->first; } } return; -- cgit v1.2.3 From 6535a9c7f4e8fec978c079d45c0349a004b73241 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Wed, 14 Feb 2024 02:04:44 +0000 Subject: Fix getting cartesian coords from triangle baripos --- game/geoData.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/game/geoData.h b/game/geoData.h index f599552..6c6ae26 100644 --- a/game/geoData.h +++ b/game/geoData.h @@ -66,7 +66,7 @@ public: { const auto & t {*this}; return t[0] + GlobalPosition(RelativePosition(t[1] - t[0]) * bari.x) - + GlobalPosition(RelativePosition(t[2] - t[1]) * bari.y); + + GlobalPosition(RelativePosition(t[2] - t[0]) * bari.y); } }; -- cgit v1.2.3 From 861c046ed527474a18a1358246a818ce70bb0b75 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Wed, 14 Feb 2024 02:07:04 +0000 Subject: Check ray intersects triangle 'in front' of start --- lib/ray.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/ray.h b/lib/ray.h index e1f43c3..67fdd12 100644 --- a/lib/ray.h +++ b/lib/ray.h @@ -47,11 +47,11 @@ public: RelativeDistance & distance) const { if constexpr (std::is_floating_point_v) { - return glm::intersectRayTriangle(start, direction, t0, t1, t2, bary, distance); + return glm::intersectRayTriangle(start, direction, t0, t1, t2, bary, distance) && distance >= 0.F; } else { const RelativePosition3D t0r = t0 - start, t1r = t1 - start, t2r = t2 - start; - return glm::intersectRayTriangle({}, direction, t0r, t1r, t2r, bary, distance); + return glm::intersectRayTriangle({}, direction, t0r, t1r, t2r, bary, distance) && distance >= 0.F; } } -- cgit v1.2.3 From 72b8e6d0b1f9ceb1fc7bd87aa71aa0dd5a15322a Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Thu, 22 Feb 2024 01:09:37 +0000 Subject: Fix calculation of texture coords of terrain While we still use this... --- game/terrain.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/game/terrain.cpp b/game/terrain.cpp index d2c8593..7f59b6c 100644 --- a/game/terrain.cpp +++ b/game/terrain.cpp @@ -34,7 +34,7 @@ Terrain::generateMeshes() [this, &vertexIndex](const GeoData::VertexHandle v) { vertexIndex.emplace(v, vertexIndex.size()); const auto p = geoData->point(v); - return Vertex {p, p / 10000, geoData->normal(v)}; + return Vertex {p, RelativePosition2D(p) / 10000.F, geoData->normal(v)}; }); std::for_each( geoData->faces_begin(), geoData->faces_end(), [this, &vertexIndex, &indices](const GeoData::FaceHandle f) { -- cgit v1.2.3 From 3770b101cfc5ad37a069850b422f0098ade7fc08 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Thu, 22 Feb 2024 01:16:29 +0000 Subject: First cut of terrain deformation This "works" for some definition of works... But it's flawed in many ways, not least the following: * It's glitchy for no obvious reason * It's unreliable; fails if you tweak the parameters * The sides of the modified area are triangulated in the dumbest possible fashion, which results in ill-formed polygons * Probably other things, but... It works, remember... --- game/geoData.cpp | 166 ++++++++++++++++++++++++++++++++++++++++++++++++++ game/geoData.h | 2 + test/test-geoData.cpp | 60 ++++++++++++++++++ 3 files changed, 228 insertions(+) diff --git a/game/geoData.cpp b/game/geoData.cpp index 0dbc0ac..86b9152 100644 --- a/game/geoData.cpp +++ b/game/geoData.cpp @@ -1,7 +1,10 @@ #include "geoData.h" +#include "collections.h" #include #include +#include #include +#include GeoData GeoData::loadFromAsciiGrid(const std::filesystem::path & input) @@ -375,3 +378,166 @@ GeoData::triangleContainsTriangle(const Triangle<2> & a, const Triangle<2> & b) { return triangleContainsPoint(a.x, b) && triangleContainsPoint(a.y, b) && triangleContainsPoint(a.z, b); } + +void +GeoData::setHeights(const std::span triangleStrip) +{ + std::set nosplit; + // Create new vertices + std::vector newVerts; + newVerts.reserve(newVerts.size()); + std::transform(triangleStrip.begin(), triangleStrip.end(), std::back_inserter(newVerts), [this](const auto tsVert) { + return add_vertex(tsVert); + }); + // Create new faces + std::vector newFaces; + newFaces.reserve(newVerts.size() - 2); + std::transform(strip_begin(newVerts), strip_end(newVerts), std::back_inserter(newFaces), + [this, &nosplit](const auto & newVert) { + const auto [a, b, c] = newVert; + auto faceHandle = add_face(a, b, c); + std::copy(fe_begin(faceHandle), fe_end(faceHandle), std::inserter(nosplit, nosplit.end())); + return faceHandle; + }); + std::vector boundary; + boundaryWalk( + [out = std::back_inserter(boundary)](const auto boundaryHeh) mutable { + out = boundaryHeh; + }, + *voh_begin(newVerts.front())); + + // Extrude corners + std::set cutpoints; + std::vector> extrusionExtents; + std::vector extrusionVertices; + std::transform(boundary.begin(), boundary.end(), std::back_inserter(extrusionExtents), + [this, &nosplit, &cutpoints, &extrusionVertices](const auto boundaryHeh) { + const auto vectorNormal + = [](const glm::vec<2, T, Q> & v) -> glm::vec<2, T, Q> { + return {-v.y, v.x}; + }; + + const auto p0 = point(from_vertex_handle(prev_halfedge_handle(boundaryHeh))); + const auto p1 = point(from_vertex_handle(boundaryHeh)); + const auto p2 = point(to_vertex_handle(boundaryHeh)); + const auto e0 = glm::normalize(vectorNormal(RelativePosition2D(p1 - p0))); + const auto e1 = glm::normalize(vectorNormal(RelativePosition2D(p2 - p1))); + const auto mid = glm::normalize((e0 + e1) / 2.F); + const auto doExtrusion = [mid, p1, this, &nosplit, &cutpoints, &extrusionVertices]( + RelativeDistance vert, GlobalDistance limit) { + const auto extrusionDir = glm::normalize(mid || vert); + + if (const auto intersect = intersectRay({p1, extrusionDir})) { + auto splitVertex = split(intersect->second, intersect->first); + cutpoints.insert(splitVertex); + extrusionVertices.push_back(splitVertex); + std::copy(ve_begin(splitVertex), ve_end(splitVertex), std::inserter(nosplit, nosplit.end())); + } + + const auto extrusion + = extrusionDir * std::max(0.F, RelativeDistance(limit - p1.z) / extrusionDir.z); + return p1 + GlobalPosition3D(extrusion); + }; + return std::make_pair(doExtrusion(-2, lowerExtent.z - 100), doExtrusion(2, upperExtent.z + 100)); + }); + + // Cut existing terrain + extrusionExtents.emplace_back(extrusionExtents.front()); // Circular next + extrusionVertices.emplace_back(extrusionVertices.front()); + std::vector> boundaryFaces; + std::transform(boundary.begin(), boundary.end(), std ::back_inserter(boundaryFaces), + [ex = extrusionExtents.begin(), exv = extrusionVertices.begin(), this, &nosplit]( + const auto boundaryHeh) mutable { + const auto fromVertex = from_vertex_handle(boundaryHeh); + const auto p0 = point(fromVertex); + auto toVertex = to_vertex_handle(boundaryHeh); + const auto p1 = point(toVertex); + const auto nex = ex + 1; + const auto nexv = exv + 1; + const std::array, 4> triangles {{ + {p0, ex->first, nex->first}, + {p0, p1, nex->first}, + {p0, ex->second, nex->second}, + {p0, p1, nex->second}, + }}; + + std::vector sideVerts {fromVertex, *exv}; + for (auto currentVertex = *exv; + std::any_of(voh_begin(currentVertex), voh_end(currentVertex), [&](const auto currentVertexOut) { + const auto next = next_halfedge_handle(currentVertexOut); + const auto nextVertex = to_vertex_handle(next); + const auto startVertex = from_vertex_handle(next); + if (nextVertex == *++sideVerts.rbegin()) { + // This half edge goes back to the previous vertex + return false; + } + if (nextVertex == *nexv) { + // The next half edge goes to the termination point + return false; + } + const auto edge = edge_handle(next); + if (nosplit.contains(edge)) { + // This edge should not be split (for some reason, maybe no longer applies) + return false; + } + const auto ep0 = point(startVertex); + const auto ep1 = point(nextVertex); + const auto diff = RelativePosition3D(ep1 - ep0); + const auto length = glm::length(diff); + const auto dir = diff / length; + const Ray r {ep0, dir}; + return std::any_of(triangles.begin(), triangles.end(), [&](const auto & triangle) { + BaryPosition bary; + RelativeDistance dist {}; + + if (r.intersectTriangle(triangle.x, triangle.y, triangle.z, bary, dist) + && dist <= length - 1 && dist >= 1) { + const auto splitPos = triangle * bary; + currentVertex = sideVerts.emplace_back(split(edge, splitPos)); + return true; + } + return false; + }); + });) { + ; + } + sideVerts.emplace_back(*nexv); + sideVerts.emplace_back(toVertex); + ex = nex; + exv++; + return sideVerts; + }); + + // Remove old faces + std::set visited; + auto removeOld = [&](auto & self, const auto face) -> void { + if (visited.insert(face).second) { + std::vector neighbourFaces; + std::for_each(fh_begin(face), fh_end(face), [&](const auto fh) { + if (std::none_of(boundaryFaces.begin(), boundaryFaces.end(), [fh, this](const auto & bf) { + return std::find(bf.begin(), bf.end(), from_vertex_handle(fh)) != bf.end() + && std::find(bf.begin(), bf.end(), to_vertex_handle(fh)) != bf.end(); + })) { + neighbourFaces.emplace_back(opposite_face_handle(fh)); + } + }); + + delete_face(face, false); + std::for_each(neighbourFaces.begin(), neighbourFaces.end(), [&self](const auto nextFace) { + if (nextFace.is_valid()) { + self(self, nextFace); + } + }); + } + }; + removeOld(removeOld, findPoint(triangleStrip.front())); + + std::for_each(boundaryFaces.begin(), boundaryFaces.end(), [&](auto & boundaryFace) { + std::reverse(boundaryFace.begin(), boundaryFace.end()); + add_face(boundaryFace); + }); + + // Tidy up + garbage_collection(); + update_vertex_normals_only(); +} diff --git a/game/geoData.h b/game/geoData.h index 6c6ae26..4d8e11c 100644 --- a/game/geoData.h +++ b/game/geoData.h @@ -89,6 +89,8 @@ public: [[nodiscard]] HalfedgeHandle findEntry(const GlobalPosition2D from, const GlobalPosition2D to) const; + void setHeights(const std::span triangleStrip); + [[nodiscard]] auto getExtents() const { diff --git a/test/test-geoData.cpp b/test/test-geoData.cpp index f8437c3..6602b05 100644 --- a/test/test-geoData.cpp +++ b/test/test-geoData.cpp @@ -1,7 +1,12 @@ #define BOOST_TEST_MODULE terrain +#include "game/terrain.h" +#include "test/testMainWindow.h" +#include "test/testRenderOutput.h" #include "testHelpers.h" +#include "ui/applicationBase.h" #include #include +#include #include #include @@ -199,3 +204,58 @@ BOOST_DATA_TEST_CASE(findEntries, { BOOST_CHECK_EQUAL(fixedTerrtain.findEntry(from, to).idx(), heh); } + +BOOST_AUTO_TEST_CASE(setTriangle, *boost::unit_test::timeout(5)) +{ + auto gd = std::make_shared(GeoData::createFlat({0, 0}, {1000000, 1000000}, 100)); + std::array points { + GlobalPosition3D {70100, 123000, 6000}, + GlobalPosition3D {50100, 52300, 6000}, + GlobalPosition3D {191000, 283000, 8000}, + GlobalPosition3D {241000, 123330, -2000}, + }; + BOOST_CHECK_NO_THROW(gd->setHeights(points)); + + ApplicationBase ab; + TestMainWindow tmw; + TestRenderOutput tro {{1792, 1024}}; + + SceneRenderer ss {tro.size, tro.output}; + ss.camera.setView({-90000, -90000, 300000}, glm::normalize(glm::vec3 {1, 1, -1.5F})); + + struct TestTerrain : public SceneProvider { + explicit TestTerrain(std::shared_ptr gd) : terrain(std::move(gd)) { } + + const Terrain terrain; + + void + content(const SceneShader & shader) const override + { + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + terrain.render(shader); + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + } + + void + environment(const SceneShader &, const SceneRenderer & sr) const override + { + sr.setAmbientLight({0.1, 0.1, 0.1}); + sr.setDirectionalLight({1, 1, 1}, south + down, *this); + } + + void + lights(const SceneShader &) const override + { + } + + void + shadows(const ShadowMapper & shadowMapper) const override + { + terrain.shadows(shadowMapper); + } + }; + + TestTerrain t {gd}; + BOOST_CHECK_NO_THROW(ss.render(t)); + Texture::save(tro.outImage, "/tmp/geoData.tga"); +} -- cgit v1.2.3