summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Goodliffe <dan@randomdan.homeip.net>2023-11-04 17:07:58 +0000
committerDan Goodliffe <dan@randomdan.homeip.net>2023-11-04 17:07:58 +0000
commit582ac127f763f512c45f35e17b768487e3b51796 (patch)
tree4b67659fd1a0906649c1f83570ab9cbb30194f75
parentReformat test source (diff)
downloadilt-582ac127f763f512c45f35e17b768487e3b51796.tar.bz2
ilt-582ac127f763f512c45f35e17b768487e3b51796.tar.xz
ilt-582ac127f763f512c45f35e17b768487e3b51796.zip
Rename TerrainMesh to GeoData to drop inplace
-rw-r--r--Jamroot.jam1
-rw-r--r--application/main.cpp16
-rw-r--r--game/geoData.cpp340
-rw-r--r--game/geoData.h115
-rw-r--r--game/terrain.cpp65
-rw-r--r--game/terrain2.cpp221
-rw-r--r--game/terrain2.h93
-rw-r--r--test/Jamfile.jam5
-rw-r--r--test/perf-geoData.cpp (renamed from test/perf-terrain.cpp)4
-rw-r--r--test/test-geo.cpp225
-rw-r--r--test/test-geoData.cpp (renamed from test/test-terrain.cpp)10
-rw-r--r--test/test-render.cpp3
-rw-r--r--ui/gameMainWindow.cpp2
13 files changed, 294 insertions, 806 deletions
diff --git a/Jamroot.jam b/Jamroot.jam
index 8a111ee..ef2f4c2 100644
--- a/Jamroot.jam
+++ b/Jamroot.jam
@@ -101,6 +101,7 @@ lib ilt :
<include>.
<include>lib
<implicit-dependency>thirdparty//glad
+ <library>OpenMeshCore
;
build-project test ;
diff --git a/application/main.cpp b/application/main.cpp
index 532c23d..9e4ca33 100644
--- a/application/main.cpp
+++ b/application/main.cpp
@@ -34,11 +34,11 @@ static const int DISPLAY_HEIGHT = 1024;
class MainApplication : public GameState, public ApplicationBase {
public:
using Windows = Collection<Window>;
+
int
run()
{
- geoData = std::make_shared<GeoData>(GeoData::Limits {{-120, -120}, {120, 120}}, 10.F);
- geoData->generateRandom();
+ geoData = std::make_shared<GeoData>(GeoData::loadFromAsciiGrid("test/fixtures/height/SD19.asc"));
Windows windows;
windows.create<GameMainWindow>(DISPLAY_WIDTH, DISPLAY_HEIGHT);
@@ -78,12 +78,12 @@ public:
train->orders.create<GoTo>(&train->orders, l3->ends[1], l3->length, rl->findNodeAt({-1100, -450, 15}));
train->currentActivity = train->orders.current()->createActivity();
- auto foliage = std::dynamic_pointer_cast<Foliage>(assets.at("Tree-01-1"));
- for (float x = 900; x < 1100; x += 3) {
- for (float y = 900; y < 1100; y += 3) {
- world.create<Plant>(foliage, Location {geoData->positionAt({-x, -y})});
- }
- }
+ // auto foliage = std::dynamic_pointer_cast<Foliage>(assets.at("Tree-01-1"));
+ // for (float x = 900; x < 1100; x += 3) {
+ // for (float y = 900; y < 1100; y += 3) {
+ // world.create<Plant>(foliage, Location {terrainMesh->positionAt(glm::vec2 {-x, -y})});
+ //}
+ //}
}
auto t_start = std::chrono::high_resolution_clock::now();
diff --git a/game/geoData.cpp b/game/geoData.cpp
index 34a1030..d32bf8c 100644
--- a/game/geoData.cpp
+++ b/game/geoData.cpp
@@ -1,214 +1,238 @@
#include "geoData.h"
-#include "gfx/image.h"
-#include <algorithm>
-#include <array>
-#include <cmath>
-#include <cstddef>
+#include <fstream>
#include <glm/gtx/intersect.hpp>
-#include <initializer_list>
-#include <limits>
#include <maths.h>
-#include <random>
-#include <ray.h>
-#include <stb/stb_image.h>
-#include <stdexcept>
-#include <util.h>
-
-GeoData::GeoData(Limits l, float s) :
- limit {std::move(l)}, size {(limit.second - limit.first) + 1}, scale {s}, nodes {[this]() {
- return (static_cast<std::size_t>(size.x * size.y));
- }()}
-{
-}
-void
-GeoData::generateRandom()
+GeoData
+GeoData::loadFromAsciiGrid(const std::filesystem::path & input)
{
- // We acknowledge this is terrible :)
-
- // Add hills
- std::mt19937 gen(std::random_device {}());
- std::uniform_int_distribution<> rxpos(limit.first.x + 2, limit.second.x - 2),
- rypos(limit.first.y + 2, limit.second.y - 2);
- std::uniform_int_distribution<> rsize(10, 30);
- std::uniform_real_distribution<float> rheight(1, 3);
- for (int h = 0; h < 500;) {
- const glm::ivec2 hpos {rxpos(gen), rypos(gen)};
- const glm::ivec2 hsize {rsize(gen), rsize(gen)};
- if (const auto lim1 = hpos - hsize; lim1.x > limit.first.x && lim1.y > limit.first.y) {
- if (const auto lim2 = hpos + hsize; lim2.x < limit.second.x && lim2.y < limit.second.y) {
- const auto height = rheight(gen);
- const glm::ivec2 hsizesqrd {hsize.x * hsize.x, hsize.y * hsize.y};
- for (auto y = lim1.y; y < lim2.y; y += 1) {
- for (auto x = lim1.x; x < lim2.x; x += 1) {
- const auto dist {hpos - glm::ivec2 {x, y}};
- const glm::ivec2 distsqrd {dist.x * dist.x, dist.y * dist.y};
- const auto out {ratio(sq(x - hpos.x), sq(hsize.x)) + ratio(sq(y - hpos.y), sq(hsize.y))};
- if (out <= 1.0F) {
- auto & node {nodes[at({x, y})]};
- const auto m {1.F / (7.F * out - 8.F) + 1.F};
- node.height += height * m;
- }
- }
- }
- h += 1;
- }
+ size_t ncols = 0, nrows = 0, xllcorner = 0, yllcorner = 0, cellsize = 0;
+ std::map<std::string_view, size_t *> properties {
+ {"ncols", &ncols},
+ {"nrows", &nrows},
+ {"xllcorner", &xllcorner},
+ {"yllcorner", &yllcorner},
+ {"cellsize", &cellsize},
+ };
+ std::ifstream f {input};
+ while (!properties.empty()) {
+ std::string property;
+ f >> property;
+ f >> *properties.at(property);
+ properties.erase(property);
+ }
+ std::vector<VertexHandle> vertices;
+ vertices.reserve(ncols * nrows);
+ GeoData mesh;
+ mesh.lowerExtent = {xllcorner, yllcorner, std::numeric_limits<float>::max()};
+ mesh.upperExtent
+ = {xllcorner + (cellsize * ncols), yllcorner + (cellsize * nrows), std::numeric_limits<float>::min()};
+ for (size_t row = 0; row < nrows; ++row) {
+ for (size_t col = 0; col < ncols; ++col) {
+ float height = 0;
+ f >> height;
+ mesh.upperExtent.z = std::max(mesh.upperExtent.z, height);
+ mesh.lowerExtent.z = std::min(mesh.lowerExtent.z, height);
+ vertices.push_back(mesh.add_vertex({xllcorner + (col * cellsize), yllcorner + (row * cellsize), height}));
}
}
-}
+ if (!f.good()) {
+ throw std::runtime_error("Couldn't read terrain file");
+ }
+ 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_face_normals();
+ mesh.update_vertex_normals();
-void
-GeoData::loadFromImages(const std::filesystem::path & fileName, float scale_)
-{
- const Image map {fileName.c_str(), STBI_grey};
- size = {map.width, map.height};
- limit = {{0, 0}, size - glm::uvec2 {1, 1}};
- const auto points {size.x * size.y};
- scale = scale_;
- nodes.resize(points);
-
- std::transform(map.data.data(), map.data.data() + points, nodes.begin(), [](auto d) {
- return Node {(d * 0.1F) - 1.5F};
- });
-}
+ return mesh;
+};
-GeoData::Quad
-GeoData::quad(glm::vec2 wcoord) const
+GeoData
+GeoData::createFlat(glm::vec2 lower, glm::vec2 upper, float h)
{
- constexpr static const std::array<glm::vec2, 4> corners {{{0, 0}, {0, 1}, {1, 0}, {1, 1}}};
- return transform_array(transform_array(corners,
- [coord = (wcoord / scale)](const auto c) {
- return glm::vec2 {std::floor(coord.x), std::floor(coord.y)} + c;
- }),
- [this](const auto c) {
- return (c * scale) || nodes[at(c)].height;
- });
-}
+ GeoData mesh;
-glm::vec3
-GeoData::positionAt(const glm::vec2 wcoord) const
-{
- const auto point {quad(wcoord)};
- const glm::vec2 frac = (wcoord - !point.front()) / scale;
- auto edge = [&point, &frac](auto offset) {
- return point[offset].z + ((point[offset + 2].z - point[offset].z) * frac.x);
- };
- const auto heightFloor = edge(0U), heightCeil = edge(1U),
- heightMid = heightFloor + ((heightCeil - heightFloor) * frac.y);
+ 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});
+
+ mesh.add_face(ll, uu, lu);
+ mesh.add_face(ll, ul, uu);
- return wcoord || heightMid;
+ mesh.update_face_normals();
+ mesh.update_vertex_normals();
+
+ return mesh;
}
-GeoData::RayTracer::RayTracer(glm::vec2 p0, glm::vec2 p1) : RayTracer {p0, p1, glm::abs(p1)} { }
-GeoData::RayTracer::RayTracer(glm::vec2 p0, glm::vec2 p1, glm::vec2 d) :
- RayTracer {p0, d, byAxis(p0, p1, d, 0), byAxis(p0, p1, d, 1)}
+OpenMesh::FaceHandle
+GeoData::findPoint(glm::vec2 p) const
{
+ return findPoint(p, *faces_begin());
}
-GeoData::RayTracer::RayTracer(
- glm::vec2 p0, glm::vec2 d_, std::pair<float, float> xdata, std::pair<float, float> ydata) :
- p {glm::floor(p0)},
- d {d_}, error {xdata.second - ydata.second}, inc {xdata.first, ydata.first}
+GeoData::PointFace::PointFace(const glm::vec2 p, const GeoData * mesh) : PointFace {p, mesh, *mesh->faces_begin()} { }
+
+GeoData::PointFace::PointFace(const glm::vec2 p, const GeoData * mesh, FaceHandle start) :
+ PointFace {p, mesh->findPoint(p, start)}
{
}
-std::pair<float, float>
-GeoData::RayTracer::byAxis(glm::vec2 p0, glm::vec2 p1, glm::vec2 d, glm::length_t axis)
+GeoData::FaceHandle
+GeoData::PointFace::face(const GeoData * mesh, FaceHandle start) const
{
- using Limits = std::numeric_limits<typename glm::vec2::value_type>;
- static_assert(Limits::has_infinity);
- if (d[axis] == 0) {
- return {0, Limits::infinity()};
- }
- else if (p1[axis] > 0) {
- return {1, (std::floor(p0[axis]) + 1.F - p0[axis]) * d[1 - axis]};
+ if (_face.is_valid()) {
+ assert(mesh->triangleContainsPoint(point, _face));
+ return _face;
}
else {
- return {-1, (p0[axis] - std::floor(p0[axis])) * d[1 - axis]};
+ return (_face = mesh->findPoint(point, start));
}
}
-glm::vec2
-GeoData::RayTracer::next()
+GeoData::FaceHandle
+GeoData::PointFace::face(const GeoData * mesh) const
{
- const glm::vec2 cur {p};
+ return face(mesh, *mesh->faces_begin());
+}
+
+namespace {
+ [[nodiscard]] constexpr inline bool
+ pointLeftOfLine(const glm::vec2 p, const glm::vec2 e1, const glm::vec2 e2)
+ {
+ return (e2.x - e1.x) * (p.y - e1.y) > (e2.y - e1.y) * (p.x - e1.x);
+ }
- static constexpr const glm::vec2 m {1, -1};
- const int axis = (error > 0) ? 1 : 0;
- p[axis] += inc[axis];
- error += d[1 - axis] * m[axis];
+ static_assert(pointLeftOfLine({1, 2}, {1, 1}, {2, 2}));
+ static_assert(pointLeftOfLine({2, 1}, {2, 2}, {1, 1}));
+ static_assert(pointLeftOfLine({2, 2}, {1, 2}, {2, 1}));
+ static_assert(pointLeftOfLine({1, 1}, {2, 1}, {1, 2}));
- return cur;
+ [[nodiscard]] constexpr inline bool
+ linesCross(const glm::vec2 a1, const glm::vec2 a2, const glm::vec2 b1, const glm::vec2 b2)
+ {
+ return pointLeftOfLine(a2, b1, b2) && pointLeftOfLine(a1, b2, b1) && pointLeftOfLine(b1, a1, a2)
+ && pointLeftOfLine(b2, a2, a1);
+ }
+
+ static_assert(linesCross({1, 1}, {2, 2}, {1, 2}, {2, 1}));
}
-std::optional<glm::vec3>
-GeoData::intersectRay(const Ray & ray) const
+OpenMesh::FaceHandle
+GeoData::findPoint(glm::vec2 p, OpenMesh::FaceHandle f) const
{
- if (glm::length(!ray.direction) <= 0) {
- return {};
- }
- RayTracer rt {ray.start / scale, ray.direction};
- while (true) {
- const auto n {rt.next() * scale};
- try {
- const auto point = quad(n);
- for (auto offset : {0U, 1U}) {
- glm::vec2 bary;
- float distance;
- if (glm::intersectRayTriangle(ray.start, ray.direction, point[offset], point[offset + 1],
- point[offset + 2], bary, distance)) {
- return point[offset] + ((point[offset + 1] - point[offset]) * bary[0])
- + ((point[offset + 2] - point[offset]) * bary[1]);
+ ConstFaceVertexIter vertices;
+ while (f.is_valid() && !triangleContainsPoint(p, vertices = cfv_iter(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;
}
}
- }
- catch (std::range_error &) {
- const auto rel = n / !ray.direction;
- if (rel.x > 0 && rel.y > 0) {
- return {};
- }
+ f.reset();
}
}
+ return f;
+}
- return {};
+glm::vec3
+GeoData::positionAt(const PointFace & p) const
+{
+ glm::vec3 out {};
+ Triangle<3> t {this, fv_range(p.face(this))};
+ glm::intersectLineTriangle(p.point ^ 0.F, up, t[0], t[1], t[2], out);
+ return p.point ^ out[0];
}
-unsigned int
-GeoData::at(glm::ivec2 coord) const
+[[nodiscard]] std::optional<glm::vec3>
+GeoData::intersectRay(const Ray & ray) const
{
- if (coord.x < limit.first.x || coord.x > limit.second.x || coord.y < limit.first.y || coord.y > limit.second.y) {
- throw std::range_error {"Coordinates outside GeoData limits"};
- }
- const glm::uvec2 offset = coord - limit.first;
- return offset.x + (offset.y * size.x);
+ return intersectRay(ray, findPoint(ray.start));
}
-unsigned int
-GeoData::at(int x, int y) const
+[[nodiscard]] std::optional<glm::vec3>
+GeoData::intersectRay(const Ray & ray, FaceHandle face) const
{
- return at({x, y});
+ std::optional<glm::vec3> out;
+ walkUntil(PointFace {ray.start, face}, ray.start + (ray.direction * 10000.F), [&out, &ray, this](FaceHandle face) {
+ glm::vec2 bari {};
+ float dist {};
+ Triangle<3> t {this, fv_range(face)};
+ if (glm::intersectRayTriangle(ray.start, ray.direction, t[0], t[1], t[2], bari, dist)) {
+ out = t * bari;
+ return true;
+ }
+ return false;
+ });
+ return out;
}
-GeoData::Limits
-GeoData::getLimit() const
+void
+GeoData::walk(const PointFace & from, const glm::vec2 to, const std::function<void(FaceHandle)> & op) const
{
- return limit;
+ walkUntil(from, to, [&op](const auto & fh) {
+ op(fh);
+ return false;
+ });
}
-float
-GeoData::getScale() const
+void
+GeoData::walkUntil(const PointFace & from, const glm::vec2 to, const std::function<bool(FaceHandle)> & op) const
+{
+ assert(from.face(this).is_valid()); // TODO replace with a boundary search
+ auto f = from.face(this);
+ FaceHandle previousFace;
+ while (f.is_valid() && !op(f)) {
+ for (auto next = cfh_iter(f); next.is_valid(); ++next) {
+ f = opposite_face_handle(*next);
+ if (f.is_valid() && f != previousFace) {
+ const auto e1 = point(to_vertex_handle(*next));
+ const auto e2 = point(to_vertex_handle(opposite_halfedge_handle(*next)));
+ if (linesCross(from.point, to, e1, e2)) {
+ previousFace = f;
+ break;
+ }
+ }
+ f.reset();
+ }
+ }
+}
+
+bool
+GeoData::triangleContainsPoint(const glm::vec2 p, const glm::vec2 a, const glm::vec2 b, const glm::vec2 c)
{
- return scale;
+ const auto det = (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x);
+
+ return det * ((b.x - a.x) * (p.y - a.y) - (b.y - a.y) * (p.x - a.x)) >= 0
+ && det * ((c.x - b.x) * (p.y - b.y) - (c.y - b.y) * (p.x - b.x)) >= 0
+ && det * ((a.x - c.x) * (p.y - c.y) - (a.y - c.y) * (p.x - c.x)) >= 0;
}
-glm::uvec2
-GeoData::getSize() const
+bool
+GeoData::triangleContainsPoint(const glm::vec2 p, FaceHandle face) const
{
- return size;
+ return triangleContainsPoint(p, cfv_iter(face));
}
-std::span<const GeoData::Node>
-GeoData::getNodes() const
+bool
+GeoData::triangleContainsPoint(const glm::vec2 p, ConstFaceVertexIter vertices) const
{
- return nodes;
+ return triangleContainsPoint(p, point(*vertices++), point(*vertices++), point(*vertices++));
}
diff --git a/game/geoData.h b/game/geoData.h
index f433a5c..3a4f98e 100644
--- a/game/geoData.h
+++ b/game/geoData.h
@@ -1,61 +1,94 @@
#pragma once
-#include <array>
+#include "collections.h" // IWYU pragma: keep IterableCollection
+#include "ray.h"
+#include <OpenMesh/Core/Mesh/TriMesh_ArrayKernelT.hh>
#include <filesystem>
-#include <glm/glm.hpp>
+#include <glm/vec2.hpp>
#include <optional>
-#include <span>
-#include <utility>
-#include <vector>
+#include <thirdparty/openmesh/glmcompat.h>
-class Ray;
+struct GeoDataTraits : public OpenMesh::DefaultTraits {
+ FaceAttributes(OpenMesh::Attributes::Normal | OpenMesh::Attributes::Status);
+ EdgeAttributes(OpenMesh::Attributes::Status);
+ VertexAttributes(OpenMesh::Attributes::Normal | OpenMesh::Attributes::Status);
+ HalfedgeAttributes(OpenMesh::Attributes::Normal | OpenMesh::Attributes::Status);
+ using Point = glm::vec3;
+ using Normal = glm::vec3;
+};
+
+class GeoData : public OpenMesh::TriMesh_ArrayKernelT<GeoDataTraits> {
+private:
+ GeoData() = default;
-class GeoData {
public:
- struct Node {
- float height {-1.5F};
- };
- using Quad = std::array<glm::vec3, 4>;
+ static GeoData loadFromAsciiGrid(const std::filesystem::path &);
+ static GeoData createFlat(glm::vec2 lower, glm::vec2, float h);
- using Limits = std::pair<glm::ivec2, glm::ivec2>;
+ struct PointFace {
+ // NOLINTNEXTLINE(hicpp-explicit-conversions)
+ PointFace(const glm::vec2 p) : point {p} { }
- explicit GeoData(Limits limit, float scale = 10.F);
+ PointFace(const glm::vec2 p, FaceHandle face) : point {p}, _face {face} { }
- void generateRandom();
- void loadFromImages(const std::filesystem::path &, float scale);
+ PointFace(const glm::vec2 p, const GeoData *);
+ PointFace(const glm::vec2 p, const GeoData *, FaceHandle start);
- [[nodiscard]] glm::vec3 positionAt(glm::vec2) const;
- [[nodiscard]] std::optional<glm::vec3> intersectRay(const Ray &) const;
+ const glm::vec2 point;
+ [[nodiscard]] FaceHandle face(const GeoData *) const;
+ [[nodiscard]] FaceHandle face(const GeoData *, FaceHandle start) const;
- [[nodiscard]] unsigned int at(glm::ivec2) const;
- [[nodiscard]] unsigned int at(int x, int y) const;
- [[nodiscard]] Quad quad(glm::vec2) const;
+ [[nodiscard]] bool
+ isLocated() const
+ {
+ return _face.is_valid();
+ }
- [[nodiscard]] Limits getLimit() const;
- [[nodiscard]] glm::uvec2 getSize() const;
- [[nodiscard]] float getScale() const;
- [[nodiscard]] std::span<const Node> getNodes() const;
+ private:
+ mutable FaceHandle _face {};
+ };
- class RayTracer {
- public:
- RayTracer(glm::vec2 p0, glm::vec2 p1);
+ template<glm::length_t Dim> struct Triangle : public glm::vec<3, glm::vec<Dim, glm::vec2::value_type>> {
+ using base = glm::vec<3, glm::vec<Dim, glm::vec2::value_type>>;
+ using base::base;
- glm::vec2 next();
+ template<IterableCollection Range> Triangle(const GeoData * m, Range range)
+ {
+ assert(std::distance(range.begin(), range.end()) == 3);
+ std::transform(range.begin(), range.end(), &base::operator[](0), [m](auto vh) {
+ return m->point(vh);
+ });
+ }
- private:
- RayTracer(glm::vec2 p0, glm::vec2 p1, glm::vec2 d);
- RayTracer(glm::vec2 p0, glm::vec2 d, std::pair<float, float>, std::pair<float, float>);
- static std::pair<float, float> byAxis(glm::vec2 p0, glm::vec2 p1, glm::vec2 d, glm::length_t);
-
- glm::vec2 p;
- const glm::vec2 d;
- float error;
- glm::vec2 inc;
+ glm::vec<Dim, glm::vec2::value_type>
+ operator*(glm::vec2 bari) const
+ {
+ const auto & t {*this};
+ return t[0] + ((t[1] - t[0]) * bari.x) + ((t[2] - t[1]) * bari.y);
+ }
};
+ [[nodiscard]] FaceHandle findPoint(glm::vec2) const;
+ [[nodiscard]] FaceHandle findPoint(glm::vec2, FaceHandle start) const;
+
+ [[nodiscard]] glm::vec3 positionAt(const PointFace &) const;
+ [[nodiscard]] std::optional<glm::vec3> intersectRay(const Ray &) const;
+ [[nodiscard]] std::optional<glm::vec3> intersectRay(const Ray &, FaceHandle start) const;
+
+ void walk(const PointFace & from, const glm::vec2 to, const std::function<void(FaceHandle)> & op) const;
+ void walkUntil(const PointFace & from, const glm::vec2 to, const std::function<bool(FaceHandle)> & op) const;
+
+ [[nodiscard]] auto
+ getExtents() const
+ {
+ return std::tie(lowerExtent, upperExtent);
+ }
+
protected:
- Limits limit {}; // Base grid limits first(x,y) -> second(x,y)
- glm::uvec2 size {};
- float scale {1};
- std::vector<Node> nodes;
+ [[nodiscard]] static bool triangleContainsPoint(const glm::vec2, const glm::vec2, const glm::vec2, const glm::vec2);
+ [[nodiscard]] bool triangleContainsPoint(const glm::vec2, FaceHandle) const;
+ [[nodiscard]] bool triangleContainsPoint(const glm::vec2, ConstFaceVertexIter) const;
+
+private:
+ glm::vec3 lowerExtent {}, upperExtent {};
};
diff --git a/game/terrain.cpp b/game/terrain.cpp
index f3bec1e..5bb8abd 100644
--- a/game/terrain.cpp
+++ b/game/terrain.cpp
@@ -2,7 +2,6 @@
#include "game/geoData.h"
#include "gfx/models/texture.h"
#include <algorithm>
-#include <array>
#include <cache.h>
#include <cstddef>
#include <filesystem>
@@ -18,8 +17,8 @@
#include <utility>
#include <vector>
-Terrain::Terrain(std::shared_ptr<GeoData> gd) :
- geoData {std::move(gd)}, grass {Texture::cachedTexture.get("grass.png")},
+Terrain::Terrain(std::shared_ptr<GeoData> tm) :
+ geoData {std::move(tm)}, grass {Texture::cachedTexture.get("grass.png")},
water {Texture::cachedTexture.get("water.png")}
{
generateMeshes();
@@ -29,51 +28,23 @@ void
Terrain::generateMeshes()
{
std::vector<unsigned int> indices;
- const auto isize = geoData->getSize() - glm::uvec2 {1, 1};
- indices.reserve(static_cast<std::size_t>(isize.x * isize.y) * 6);
-
- const auto limit = geoData->getLimit();
- // Indices
- constexpr std::array<glm::ivec2, 6> indices_offsets {{
- {0, 0},
- {1, 0},
- {1, 1},
- {0, 0},
- {1, 1},
- {0, 1},
- }};
- for (auto y = limit.first.y; y < limit.second.y; y += 1) {
- for (auto x = limit.first.x; x < limit.second.x; x += 1) {
- std::transform(indices_offsets.begin(), indices_offsets.end(), std::back_inserter(indices),
- [this, x, y](const auto off) {
- return geoData->at(x + off.x, y + off.y);
- });
- }
- }
-
- const auto nodes = geoData->getNodes();
- const auto scale = geoData->getScale();
+ indices.reserve(geoData->n_faces() * 3);
std::vector<Vertex> vertices;
- vertices.reserve(nodes.size());
- // Positions
- for (auto y = limit.first.y; y <= limit.second.y; y += 1) {
- for (auto x = limit.first.x; x <= limit.second.x; x += 1) {
- const glm::vec2 xy {x, y};
- vertices.emplace_back((xy * scale) ^ nodes[geoData->at(x, y)].height, xy, ::up);
- }
- }
- // Normals
- const glm::uvec2 size = geoData->getSize();
- for (auto y = limit.first.y + 1; y < limit.second.y; y += 1) {
- for (auto x = limit.first.x + 1; x < limit.second.x; x += 1) {
- const auto n {geoData->at(x, y)};
- const auto a = vertices[n - 1].pos;
- const auto b = vertices[n - size.x].pos;
- const auto c = vertices[n + 1].pos;
- const auto d = vertices[n + size.x].pos;
- vertices[n].normal = -glm::normalize(glm::cross(b - d, a - c));
- }
- }
+ vertices.reserve(geoData->n_vertices());
+ std::map<GeoData::VertexHandle, size_t> vertexIndex;
+ std::transform(geoData->vertices_begin(), geoData->vertices_end(), std::back_inserter(vertices),
+ [this, &vertexIndex](const GeoData::VertexHandle v) {
+ vertexIndex.emplace(v, vertexIndex.size());
+ const glm::vec3 p = geoData->point(v);
+ return Vertex {p, p / 10.F, geoData->normal(v)};
+ });
+ std::for_each(
+ geoData->faces_begin(), geoData->faces_end(), [this, &vertexIndex, &indices](const GeoData::FaceHandle f) {
+ std::transform(geoData->fv_begin(f), geoData->fv_end(f), std::back_inserter(indices),
+ [&vertexIndex](const GeoData::VertexHandle v) {
+ return vertexIndex[v];
+ });
+ });
meshes.create<Mesh>(vertices, indices);
}
diff --git a/game/terrain2.cpp b/game/terrain2.cpp
deleted file mode 100644
index 0e8b78e..0000000
--- a/game/terrain2.cpp
+++ /dev/null
@@ -1,221 +0,0 @@
-#include "terrain2.h"
-#include <fstream>
-#include <glm/gtx/intersect.hpp>
-#include <maths.h>
-
-TerrainMesh
-TerrainMesh::loadFromAsciiGrid(const std::filesystem::path & input)
-{
- size_t ncols = 0, nrows = 0, xllcorner = 0, yllcorner = 0, cellsize = 0;
- std::map<std::string_view, size_t *> properties {
- {"ncols", &ncols},
- {"nrows", &nrows},
- {"xllcorner", &xllcorner},
- {"yllcorner", &yllcorner},
- {"cellsize", &cellsize},
- };
- std::ifstream f {input};
- while (!properties.empty()) {
- std::string property;
- f >> property;
- f >> *properties.at(property);
- properties.erase(property);
- }
- std::vector<VertexHandle> vertices;
- vertices.reserve(ncols * nrows);
- TerrainMesh mesh;
- mesh.lowerExtent = {xllcorner, yllcorner, std::numeric_limits<float>::max()};
- mesh.upperExtent
- = {xllcorner + (cellsize * ncols), yllcorner + (cellsize * nrows), std::numeric_limits<float>::min()};
- for (size_t row = 0; row < nrows; ++row) {
- for (size_t col = 0; col < ncols; ++col) {
- float height = 0;
- f >> height;
- mesh.upperExtent.z = std::max(mesh.upperExtent.z, height);
- mesh.lowerExtent.z = std::min(mesh.lowerExtent.z, height);
- vertices.push_back(mesh.add_vertex({xllcorner + (col * cellsize), yllcorner + (row * cellsize), height}));
- }
- }
- if (!f.good()) {
- throw std::runtime_error("Couldn't read terrain file");
- }
- 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_face_normals();
- mesh.update_vertex_normals();
-
- return mesh;
-};
-
-OpenMesh::FaceHandle
-TerrainMesh::findPoint(glm::vec2 p) const
-{
- return findPoint(p, *faces_begin());
-}
-
-TerrainMesh::PointFace::PointFace(const glm::vec2 p, const TerrainMesh * mesh) :
- PointFace {p, mesh, *mesh->faces_begin()}
-{
-}
-
-TerrainMesh::PointFace::PointFace(const glm::vec2 p, const TerrainMesh * mesh, FaceHandle start) :
- PointFace {p, mesh->findPoint(p, start)}
-{
-}
-
-TerrainMesh::FaceHandle
-TerrainMesh::PointFace::face(const TerrainMesh * mesh, FaceHandle start) const
-{
- if (_face.is_valid()) {
- assert(mesh->triangleContainsPoint(point, _face));
- return _face;
- }
- else {
- return (_face = mesh->findPoint(point, start));
- }
-}
-
-TerrainMesh::FaceHandle
-TerrainMesh::PointFace::face(const TerrainMesh * mesh) const
-{
- return face(mesh, *mesh->faces_begin());
-}
-
-namespace {
- [[nodiscard]] constexpr inline bool
- pointLeftOfLine(const glm::vec2 p, const glm::vec2 e1, const glm::vec2 e2)
- {
- return (e2.x - e1.x) * (p.y - e1.y) > (e2.y - e1.y) * (p.x - e1.x);
- }
-
- static_assert(pointLeftOfLine({1, 2}, {1, 1}, {2, 2}));
- static_assert(pointLeftOfLine({2, 1}, {2, 2}, {1, 1}));
- static_assert(pointLeftOfLine({2, 2}, {1, 2}, {2, 1}));
- static_assert(pointLeftOfLine({1, 1}, {2, 1}, {1, 2}));
-
- [[nodiscard]] constexpr inline bool
- linesCross(const glm::vec2 a1, const glm::vec2 a2, const glm::vec2 b1, const glm::vec2 b2)
- {
- return pointLeftOfLine(a2, b1, b2) && pointLeftOfLine(a1, b2, b1) && pointLeftOfLine(b1, a1, a2)
- && pointLeftOfLine(b2, a2, a1);
- }
-
- static_assert(linesCross({1, 1}, {2, 2}, {1, 2}, {2, 1}));
-}
-
-OpenMesh::FaceHandle
-TerrainMesh::findPoint(glm::vec2 p, OpenMesh::FaceHandle f) const
-{
- ConstFaceVertexIter vertices;
- while (f.is_valid() && !triangleContainsPoint(p, vertices = cfv_iter(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;
-}
-
-glm::vec3
-TerrainMesh::positionAt(const PointFace & p) const
-{
- glm::vec3 out {};
- Triangle<3> t {this, fv_range(p.face(this))};
- glm::intersectLineTriangle(p.point ^ 0.F, up, t[0], t[1], t[2], out);
- return p.point ^ out[0];
-}
-
-[[nodiscard]] std::optional<glm::vec3>
-TerrainMesh::intersectRay(const Ray & ray) const
-{
- return intersectRay(ray, findPoint(ray.start));
-}
-
-[[nodiscard]] std::optional<glm::vec3>
-TerrainMesh::intersectRay(const Ray & ray, FaceHandle face) const
-{
- std::optional<glm::vec3> out;
- walkUntil(PointFace {ray.start, face}, ray.start + (ray.direction * 10000.F), [&out, &ray, this](FaceHandle face) {
- glm::vec2 bari {};
- float dist {};
- Triangle<3> t {this, fv_range(face)};
- if (glm::intersectRayTriangle(ray.start, ray.direction, t[0], t[1], t[2], bari, dist)) {
- out = t * bari;
- return true;
- }
- return false;
- });
- return out;
-}
-
-void
-TerrainMesh::walk(const PointFace & from, const glm::vec2 to, const std::function<void(FaceHandle)> & op) const
-{
- walkUntil(from, to, [&op](const auto & fh) {
- op(fh);
- return false;
- });
-}
-
-void
-TerrainMesh::walkUntil(const PointFace & from, const glm::vec2 to, const std::function<bool(FaceHandle)> & op) const
-{
- assert(from.face(this).is_valid()); // TODO replace with a boundary search
- auto f = from.face(this);
- FaceHandle previousFace;
- while (f.is_valid() && !op(f)) {
- for (auto next = cfh_iter(f); next.is_valid(); ++next) {
- f = opposite_face_handle(*next);
- if (f.is_valid() && f != previousFace) {
- const auto e1 = point(to_vertex_handle(*next));
- const auto e2 = point(to_vertex_handle(opposite_halfedge_handle(*next)));
- if (linesCross(from.point, to, e1, e2)) {
- previousFace = f;
- break;
- }
- }
- f.reset();
- }
- }
-}
-
-bool
-TerrainMesh::triangleContainsPoint(const glm::vec2 p, const glm::vec2 a, const glm::vec2 b, const glm::vec2 c)
-{
- const auto det = (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x);
-
- return det * ((b.x - a.x) * (p.y - a.y) - (b.y - a.y) * (p.x - a.x)) >= 0
- && det * ((c.x - b.x) * (p.y - b.y) - (c.y - b.y) * (p.x - b.x)) >= 0
- && det * ((a.x - c.x) * (p.y - c.y) - (a.y - c.y) * (p.x - c.x)) >= 0;
-}
-
-bool
-TerrainMesh::triangleContainsPoint(const glm::vec2 p, FaceHandle face) const
-{
- return triangleContainsPoint(p, cfv_iter(face));
-}
-
-bool
-TerrainMesh::triangleContainsPoint(const glm::vec2 p, ConstFaceVertexIter vertices) const
-{
- return triangleContainsPoint(p, point(*vertices++), point(*vertices++), point(*vertices++));
-}
diff --git a/game/terrain2.h b/game/terrain2.h
deleted file mode 100644
index eda56d0..0000000
--- a/game/terrain2.h
+++ /dev/null
@@ -1,93 +0,0 @@
-#pragma once
-
-#include "collections.h" // IWYU pragma: keep IterableCollection
-#include "ray.h"
-#include <OpenMesh/Core/Mesh/TriMesh_ArrayKernelT.hh>
-#include <filesystem>
-#include <glm/vec2.hpp>
-#include <optional>
-#include <thirdparty/openmesh/glmcompat.h>
-
-struct TerrainTraits : public OpenMesh::DefaultTraits {
- FaceAttributes(OpenMesh::Attributes::Normal | OpenMesh::Attributes::Status);
- EdgeAttributes(OpenMesh::Attributes::Status);
- VertexAttributes(OpenMesh::Attributes::Normal | OpenMesh::Attributes::Status);
- HalfedgeAttributes(OpenMesh::Attributes::Normal | OpenMesh::Attributes::Status);
- using Point = glm::vec3;
- using Normal = glm::vec3;
-};
-
-class TerrainMesh : public OpenMesh::TriMesh_ArrayKernelT<TerrainTraits> {
-private:
- TerrainMesh() = default;
-
-public:
- static TerrainMesh loadFromAsciiGrid(const std::filesystem::path &);
-
- struct PointFace {
- // NOLINTNEXTLINE(hicpp-explicit-conversions)
- PointFace(const glm::vec2 p) : point {p} { }
-
- PointFace(const glm::vec2 p, FaceHandle face) : point {p}, _face {face} { }
-
- PointFace(const glm::vec2 p, const TerrainMesh *);
- PointFace(const glm::vec2 p, const TerrainMesh *, FaceHandle start);
-
- const glm::vec2 point;
- [[nodiscard]] FaceHandle face(const TerrainMesh *) const;
- [[nodiscard]] FaceHandle face(const TerrainMesh *, FaceHandle start) const;
-
- [[nodiscard]] bool
- isLocated() const
- {
- return _face.is_valid();
- }
-
- private:
- mutable FaceHandle _face {};
- };
-
- template<glm::length_t Dim> struct Triangle : public glm::vec<3, glm::vec<Dim, glm::vec2::value_type>> {
- using base = glm::vec<3, glm::vec<Dim, glm::vec2::value_type>>;
- using base::base;
-
- template<IterableCollection Range> Triangle(const TerrainMesh * m, Range range)
- {
- assert(std::distance(range.begin(), range.end()) == 3);
- std::transform(range.begin(), range.end(), &base::operator[](0), [m](auto vh) {
- return m->point(vh);
- });
- }
-
- glm::vec<Dim, glm::vec2::value_type>
- operator*(glm::vec2 bari) const
- {
- const auto & t {*this};
- return t[0] + ((t[1] - t[0]) * bari.x) + ((t[2] - t[1]) * bari.y);
- }
- };
-
- [[nodiscard]] FaceHandle findPoint(glm::vec2) const;
- [[nodiscard]] FaceHandle findPoint(glm::vec2, FaceHandle start) const;
-
- [[nodiscard]] glm::vec3 positionAt(const PointFace &) const;
- [[nodiscard]] std::optional<glm::vec3> intersectRay(const Ray &) const;
- [[nodiscard]] std::optional<glm::vec3> intersectRay(const Ray &, FaceHandle start) const;
-
- void walk(const PointFace & from, const glm::vec2 to, const std::function<void(FaceHandle)> & op) const;
- void walkUntil(const PointFace & from, const glm::vec2 to, const std::function<bool(FaceHandle)> & op) const;
-
- [[nodiscard]] auto
- getExtents() const
- {
- return std::tie(lowerExtent, upperExtent);
- }
-
-protected:
- [[nodiscard]] static bool triangleContainsPoint(const glm::vec2, const glm::vec2, const glm::vec2, const glm::vec2);
- [[nodiscard]] bool triangleContainsPoint(const glm::vec2, FaceHandle) const;
- [[nodiscard]] bool triangleContainsPoint(const glm::vec2, ConstFaceVertexIter) const;
-
-private:
- glm::vec3 lowerExtent {}, upperExtent {};
-};
diff --git a/test/Jamfile.jam b/test/Jamfile.jam
index d88a238..6f62056 100644
--- a/test/Jamfile.jam
+++ b/test/Jamfile.jam
@@ -46,7 +46,7 @@ lib test : [ glob *.cpp : test-*.cpp perf-*.cpp ] ;
run test-collection.cpp ;
run test-maths.cpp ;
run test-lib.cpp ;
-run test-geo.cpp ;
+run test-geoData.cpp : -- : fixtures/height/SD19.asc : <library>test ;
run test-network.cpp : : : <library>test ;
run test-persistence.cpp : -- : [ sequence.insertion-sort [ glob-tree fixtures : *.json ] ] : <library>test ;
run test-text.cpp : : : <library>test ;
@@ -55,13 +55,12 @@ run test-render.cpp : -- : test-assetFactory : <library>test ;
run test-glContextBhvr.cpp ;
run test-assetFactory.cpp : -- : [ sequence.insertion-sort [ glob-tree $(res) : *.* ] fixtures/rgb.txt test-instancing ] : <library>test ;
run perf-assetFactory.cpp : -- : test-assetFactory : <library>benchmark <library>test ;
+run perf-geoData.cpp : : : <library>test <library>benchmark ;
run perf-persistence.cpp : -- : test-persistence : <library>benchmark <library>test ;
run test-worker.cpp ;
run test-instancing.cpp : : : <library>test ;
run test-glContainer.cpp : : : <library>test ;
run test-pack.cpp : : : <library>test ;
-run test-terrain.cpp : -- : fixtures/height/SD19.asc : <library>test <library>..//OpenMeshCore ;
-run perf-terrain.cpp : : : <library>test <library>..//OpenMeshCore <library>benchmark ;
compile test-static-enumDetails.cpp ;
compile test-static-stream_support.cpp ;
explicit perf-assetFactory ;
diff --git a/test/perf-terrain.cpp b/test/perf-geoData.cpp
index e998f60..e1546c1 100644
--- a/test/perf-terrain.cpp
+++ b/test/perf-geoData.cpp
@@ -1,8 +1,8 @@
#include <benchmark/benchmark.h>
-#include <game/terrain2.h>
+#include <game/geoData.h>
namespace {
- const TerrainMesh tm {FIXTURESDIR "height/SD19.asc"};
+ const GeoData tm {GeoData::loadFromAsciiGrid(FIXTURESDIR "height/SD19.asc")};
void
terrain_findPoint(benchmark::State & state)
diff --git a/test/test-geo.cpp b/test/test-geo.cpp
deleted file mode 100644
index 9874fb7..0000000
--- a/test/test-geo.cpp
+++ /dev/null
@@ -1,225 +0,0 @@
-#define BOOST_TEST_MODULE test_geo
-
-#include "testHelpers.h"
-#include <boost/test/data/test_case.hpp>
-#include <boost/test/unit_test.hpp>
-#include <stream_support.h>
-
-#include <game/geoData.h>
-#include <lib/ray.h>
-
-struct TestGeoData : public GeoData {
- TestGeoData() : GeoData {{{-10, -5}, {30, 40}}, 5.F} { }
-};
-
-namespace std {
- std::ostream &
- operator<<(std::ostream & s, const Ray & r)
- {
- return (s << r.start << "->" << r.direction);
- }
-}
-
-BOOST_FIXTURE_TEST_SUITE(tgd, TestGeoData)
-
-BOOST_AUTO_TEST_CASE(initialize)
-{
- BOOST_CHECK_EQUAL(limit.first, glm::ivec2(-10, -5));
- BOOST_CHECK_EQUAL(limit.second, glm::ivec2(30, 40));
- BOOST_CHECK_EQUAL(scale, 5.F);
- BOOST_CHECK_EQUAL(size, glm::uvec2(41, 46));
- BOOST_CHECK_EQUAL(nodes.size(), 1886);
- BOOST_CHECK(std::all_of(nodes.begin(), nodes.end(), [](const auto & n) {
- return n.height == -1.5F;
- }));
-}
-
-BOOST_AUTO_TEST_CASE(coords)
-{
- BOOST_CHECK_EQUAL(at(-10, -5), 0);
- BOOST_CHECK_EQUAL(at(-9, -5), 1);
- BOOST_CHECK_EQUAL(at(0, -5), 10);
- BOOST_CHECK_EQUAL(at(30, -5), 40);
- BOOST_CHECK_EQUAL(at(30, 40), 1885);
-}
-
-BOOST_AUTO_TEST_CASE(coords_bad)
-{
- BOOST_CHECK_THROW(std::ignore = at(-11, -5), std::range_error);
- BOOST_CHECK_THROW(std::ignore = at(-10, -6), std::range_error);
- BOOST_CHECK_THROW(std::ignore = at(-11, -6), std::range_error);
- BOOST_CHECK_THROW(std::ignore = at(31, 40), std::range_error);
- BOOST_CHECK_THROW(std::ignore = at(30, 41), std::range_error);
- BOOST_CHECK_THROW(std::ignore = at(31, 41), std::range_error);
-}
-
-BOOST_AUTO_TEST_CASE(gen_random)
-{
- // Can only really its sanity
- generateRandom();
- // Some terrain above sea level
- BOOST_CHECK(std::any_of(nodes.begin(), nodes.end(), [](const auto & n) {
- return n.height > 0;
- }));
- // Still an island
- for (int x = limit.first.x; x <= limit.second.x; x += 1) {
- BOOST_CHECK_EQUAL(nodes[at(x, limit.first.y)].height, -1.5F);
- BOOST_CHECK_EQUAL(nodes[at(x, limit.second.y)].height, -1.5F);
- }
- for (int y = limit.first.y; y <= limit.second.y; y += 1) {
- BOOST_CHECK_EQUAL(nodes[at(limit.first.x, y)].height, -1.5F);
- BOOST_CHECK_EQUAL(nodes[at(limit.second.x, y)].height, -1.5F);
- }
-}
-
-BOOST_AUTO_TEST_CASE(load_uk_heightmap)
-{
- loadFromImages(FIXTURESDIR "/height/V0txo.jpg", 100.F);
- // Some terrain above sea level
- BOOST_CHECK(std::any_of(nodes.begin(), nodes.end(), [](const auto & n) {
- return n.height > 0;
- }));
-}
-
-BOOST_AUTO_TEST_CASE(get_height_at)
-{
- // at(x,y) is index based
- nodes[at(0, 0)].height = 1;
- nodes[at(1, 0)].height = 2;
- nodes[at(0, 1)].height = 3;
- nodes[at(1, 1)].height = 4;
-
- // positionAt(x,y) is world based
- // Corners
- BOOST_CHECK_EQUAL(positionAt({0, 0}), glm::vec3(0, 0, 1));
- BOOST_CHECK_EQUAL(positionAt({5, 0}), glm::vec3(5, 0, 2));
- BOOST_CHECK_EQUAL(positionAt({0, 5}), glm::vec3(0, 5, 3));
- BOOST_CHECK_EQUAL(positionAt({5, 5}), glm::vec3(5, 5, 4));
-
- // Edge midpoints
- BOOST_CHECK_EQUAL(positionAt({0, 2.5F}), glm::vec3(0, 2.5F, 2));
- BOOST_CHECK_EQUAL(positionAt({5, 2.5F}), glm::vec3(5, 2.5F, 3));
- BOOST_CHECK_EQUAL(positionAt({2.5F, 0}), glm::vec3(2.5F, 0, 1.5F));
- BOOST_CHECK_EQUAL(positionAt({2.5F, 5}), glm::vec3(2.5F, 5, 3.5F));
-
- // Centre
- BOOST_CHECK_EQUAL(positionAt({2.5F, 2.5F}), glm::vec3(2.5F, 2.5F, 2.5F));
-}
-
-using TestRayTracerData = std::tuple<glm::vec2, glm::vec2, glm::vec2::value_type, std::vector<glm::vec2>>;
-BOOST_TEST_DECORATOR(*boost::unit_test::timeout(1))
-
-BOOST_DATA_TEST_CASE(raytracer,
- boost::unit_test::data::make<TestRayTracerData>({
- {{1, 2}, {4, 5}, 4,
- {
- {0, 0},
- {0, 4},
- {4, 4},
- {4, 8},
- {8, 8},
- {8, 12},
- {12, 12},
- {12, 16},
- {12, 20},
- {16, 20},
- }},
- {{-1, -1}, {-4, -5}, 5,
- {
- {-5, -5},
- {-5, -10},
- {-10, -10},
- {-10, -15},
- {-15, -15},
- {-15, -20},
- {-20, -20},
- {-20, -25},
- }},
- }),
- start, dir, scale, points)
-{
- GeoData::RayTracer rt {start / scale, glm::normalize(dir)};
- for (const auto & point : points) {
- BOOST_CHECK_CLOSE_VEC(point, rt.next() * scale);
- }
-}
-
-using TestRayData = std::tuple<glm::vec3, glm::vec3, glm::vec3>;
-BOOST_TEST_DECORATOR(*boost::unit_test::timeout(1))
-
-BOOST_DATA_TEST_CASE(intersect_ray,
- boost::unit_test::data::make<TestRayData>({
- {{-1, -1, 1.0}, {1, 1, 0}, {0, 0, 1}},
- {{-1, -1, 2.5}, {1, 1, 0}, {2.5F, 2.5F, 2.5F}},
- {{-20, -20, 2.5}, {1, 1, 0}, {2.5F, 2.5F, 2.5F}},
- // outside the map looking in
- {{-205, -205, 4}, {1, 1, 0}, {5, 5, 4}},
- {{-205, 5, 4}, {1, 0, 0}, {5, 5, 4}},
- {{-205, 215, 4}, {1, -1, 0}, {5, 5, 4}},
- {{215, -205, 4}, {-1, 1, 0}, {5, 5, 4}},
- {{215, 5, 4}, {-1, 0, 0}, {5, 5, 4}},
- {{215, 215, 4}, {-1, -1, 0}, {5, 5, 4}},
- {{5, 215, 4}, {0, -1, 0}, {5, 5, 4}},
- {{5, -205, 4}, {0, 1, 0}, {5, 5, 4}},
- }),
- start, dir, pos)
-{
- // at(x,y) is index based
- nodes[at(0, 0)].height = 1;
- nodes[at(1, 0)].height = 2;
- nodes[at(0, 1)].height = 3;
- nodes[at(1, 1)].height = 4;
-
- const auto intersect = intersectRay({start, glm::normalize(dir)});
-
- BOOST_CHECK_IF(has_intersect, intersect) {
- BOOST_CHECK_CLOSE_VEC(*intersect, pos);
- }
-}
-
-auto xs = boost::unit_test::data::xrange(-20.F, 0.F, 0.6F), ys = boost::unit_test::data::xrange(-20.F, 0.F, 0.7F);
-auto targetsx = boost::unit_test::data::xrange(0.2F, 4.9F, 1.3F),
- targetsy = boost::unit_test::data::xrange(0.3F, 4.9F, 1.3F);
-BOOST_TEST_DECORATOR(*boost::unit_test::timeout(1))
-
-BOOST_DATA_TEST_CASE(intersect_ray_many, xs * ys * targetsx * targetsy, x, y, targetx, targety)
-{
- // at(x,y) is index based
- nodes[at(0, 0)].height = 1;
- nodes[at(1, 0)].height = 2;
- nodes[at(0, 1)].height = 3;
- nodes[at(1, 1)].height = 4;
-
- const glm::vec3 start {x, y, 10};
- const auto target {this->positionAt({targetx, targety})};
- const Ray ray {start, glm::normalize(target - start)};
-
- BOOST_TEST_CONTEXT(ray) {
- const auto intersect = intersectRay(ray);
-
- BOOST_CHECK_IF(has_intersect, intersect) {
- BOOST_CHECK_CLOSE_VEC(*intersect, target);
- }
- }
-}
-
-BOOST_TEST_DECORATOR(*boost::unit_test::timeout(1))
-
-BOOST_DATA_TEST_CASE(intersect_ray_miss,
- boost::unit_test::data::make<Ray>({
- {{3, 3, 5}, {-1, -1, 0}},
- {{0, 0, 5}, {0, 0, 1}},
- {{0, 0, 5}, {0, 0, -1}},
- }),
- ray)
-{
- // at(x,y) is index based
- nodes[at(0, 0)].height = 1;
- nodes[at(1, 0)].height = 2;
- nodes[at(0, 1)].height = 3;
- nodes[at(1, 1)].height = 4;
-
- BOOST_REQUIRE(!intersectRay(ray));
-}
-
-BOOST_AUTO_TEST_SUITE_END()
diff --git a/test/test-terrain.cpp b/test/test-geoData.cpp
index 71fc1ec..d79f5b8 100644
--- a/test/test-terrain.cpp
+++ b/test/test-geoData.cpp
@@ -4,11 +4,11 @@
#include <boost/test/unit_test.hpp>
#include <stream_support.h>
-#include <game/terrain2.h>
+#include <game/geoData.h>
-class TestTerrainMesh : public TerrainMesh {
+class TestTerrainMesh : public GeoData {
public:
- TestTerrainMesh() : TerrainMesh {TerrainMesh::loadFromAsciiGrid(FIXTURESDIR "height/SD19.asc")} { }
+ TestTerrainMesh() : GeoData {GeoData::loadFromAsciiGrid(FIXTURESDIR "height/SD19.asc")} { }
};
constexpr size_t ncols = 200, nrows = 200, xllcorner = 310000, yllcorner = 490000, cellsize = 50;
@@ -82,7 +82,7 @@ BOOST_DATA_TEST_CASE(findPointOnTerrain,
{0, 1, 2, 3, 4, 5, 6, 10, 100, 150, 200, 1000, 1234, 17439, 79201, 79200, 79199}),
p, fh, start)
{
- BOOST_CHECK_EQUAL(fh, fixedTerrtain.findPoint(p, TerrainMesh::FaceHandle(start)).idx());
+ BOOST_CHECK_EQUAL(fh, fixedTerrtain.findPoint(p, GeoData::FaceHandle(start)).idx());
}
using FindPositionData = std::tuple<glm::vec2, float>;
@@ -149,7 +149,7 @@ BOOST_DATA_TEST_CASE(walkTerrainSetsFromFace,
}),
from, to, visits)
{
- TerrainMesh::PointFace pf {from};
+ GeoData::PointFace pf {from};
BOOST_CHECK_NO_THROW(fixedTerrtain.walk(pf, to, [](auto) {}));
BOOST_CHECK_EQUAL(pf.face(&fixedTerrtain).idx(), visits.front());
}
diff --git a/test/test-render.cpp b/test/test-render.cpp
index b16f241..fb2a71b 100644
--- a/test/test-render.cpp
+++ b/test/test-render.cpp
@@ -26,8 +26,7 @@ class TestScene : public SceneProvider {
std::shared_ptr<RailVehicle> train1, train2;
Terrain terrain {[]() {
- auto gd = std::make_shared<GeoData>(GeoData::Limits {{0, 0}, {100, 100}});
- gd->generateRandom();
+ auto gd = std::make_shared<GeoData>(GeoData::createFlat({0, 0}, {1000, 1000}, 1));
return gd;
}()};
diff --git a/ui/gameMainWindow.cpp b/ui/gameMainWindow.cpp
index c35c9c6..6981c54 100644
--- a/ui/gameMainWindow.cpp
+++ b/ui/gameMainWindow.cpp
@@ -31,7 +31,7 @@ public:
GameMainWindow::GameMainWindow(size_t w, size_t h) :
Window {w, h, "I Like Trains", SDL_WINDOW_OPENGL}, SceneRenderer {Window::size, 0}
{
- uiComponents.create<ManualCameraController>(glm::vec2 {-1150, -1150});
+ uiComponents.create<ManualCameraController>(glm::vec2 {315000, 495000});
auto gms = uiComponents.create<GameMainSelector>(&camera, glm::vec2 {w, h});
uiComponents.create<GameMainToolbar>(gms.get());
}