summaryrefslogtreecommitdiff
path: root/game
diff options
context:
space:
mode:
authorDan Goodliffe <dan@randomdan.homeip.net>2024-01-01 17:56:26 +0000
committerDan Goodliffe <dan@randomdan.homeip.net>2024-01-01 17:56:26 +0000
commitd5cdbbf38380239524e351cb69aec94090884ca5 (patch)
tree5d7dff2f2775701069806eceb4eaef23b22eba3f /game
parentReformat with new clang-format (diff)
parentRemove more use of legacy types (diff)
downloadilt-d5cdbbf38380239524e351cb69aec94090884ca5.tar.bz2
ilt-d5cdbbf38380239524e351cb69aec94090884ca5.tar.xz
ilt-d5cdbbf38380239524e351cb69aec94090884ca5.zip
Merge remote-tracking branch 'origin/terrain'
Diffstat (limited to 'game')
-rw-r--r--game/geoData.cpp432
-rw-r--r--game/geoData.h134
-rw-r--r--game/network/link.cpp16
-rw-r--r--game/network/link.h12
-rw-r--r--game/network/network.cpp24
-rw-r--r--game/network/network.h44
-rw-r--r--game/network/network.impl.h12
-rw-r--r--game/network/rail.cpp50
-rw-r--r--game/network/rail.h10
-rw-r--r--game/scenary/foliage.cpp3
-rw-r--r--game/scenary/foliage.h3
-rw-r--r--game/scenary/plant.cpp2
-rw-r--r--game/scenary/plant.h2
-rw-r--r--game/selectable.h3
-rw-r--r--game/terrain.cpp65
-rw-r--r--game/vehicles/linkHistory.cpp2
-rw-r--r--game/vehicles/railVehicle.cpp54
-rw-r--r--game/vehicles/railVehicle.h7
-rw-r--r--game/vehicles/railVehicleClass.cpp12
-rw-r--r--game/vehicles/railVehicleClass.h1
-rw-r--r--game/vehicles/train.cpp2
-rw-r--r--game/vehicles/train.h2
22 files changed, 528 insertions, 364 deletions
diff --git a/game/geoData.cpp b/game/geoData.cpp
index 76550cc..97f4a26 100644
--- a/game/geoData.cpp
+++ b/game/geoData.cpp
@@ -1,215 +1,341 @@
#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));
- }()}
+GeoData
+GeoData::loadFromAsciiGrid(const std::filesystem::path & input)
{
-}
-
-void
-GeoData::generateRandom()
-{
- // 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);
+ }
+ xllcorner *= 1000;
+ yllcorner *= 1000;
+ cellsize *= 1000;
+ std::vector<VertexHandle> vertices;
+ vertices.reserve(ncols * nrows);
+ GeoData mesh;
+ mesh.lowerExtent = {xllcorner, yllcorner, std::numeric_limits<GlobalDistance>::max()};
+ mesh.upperExtent = {xllcorner + (cellsize * (ncols - 1)), yllcorner + (cellsize * (nrows - 1)),
+ std::numeric_limits<GlobalDistance>::min()};
+ for (size_t row = 0; row < nrows; ++row) {
+ for (size_t col = 0; col < ncols; ++col) {
+ float heightf = 0;
+ f >> heightf;
+ const auto height = static_cast<GlobalDistance>(std::round(heightf * 1000.F));
+ 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_vertex_normals_only();
-void
-GeoData::loadFromImages(const std::filesystem::path & fileName, float scale_)
+ return mesh;
+};
+
+GeoData
+GeoData::createFlat(GlobalPosition2D lower, GlobalPosition2D upper, GlobalDistance h)
{
- 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);
+ GeoData mesh;
- std::transform(map.data.data(), map.data.data() + points, nodes.begin(), [](auto d) {
- return Node {(d * 0.1F) - 1.5F};
- });
+ 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);
+
+ mesh.update_vertex_normals_only();
+
+ return mesh;
}
-GeoData::Quad
-GeoData::quad(glm::vec2 wcoord) const
+OpenMesh::FaceHandle
+GeoData::findPoint(GlobalPosition2D p) const
{
- 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;
- });
+ return findPoint(p, *faces_begin());
}
-glm::vec3
-GeoData::positionAt(const glm::vec2 wcoord) const
+GeoData::PointFace::PointFace(const GlobalPosition2D p, const GeoData * mesh) :
+ PointFace {p, mesh, *mesh->faces_begin()}
{
- 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);
-
- return wcoord || heightMid;
}
-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)}
+GeoData::PointFace::PointFace(const GlobalPosition2D p, const GeoData * mesh, FaceHandle start) :
+ PointFace {p, mesh->findPoint(p, start)}
{
}
-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::FaceHandle
+GeoData::PointFace::face(const GeoData * mesh, FaceHandle start) const
{
+ if (_face.is_valid()) {
+ assert(mesh->triangleContainsPoint(point, _face));
+ return _face;
+ }
+ else {
+ return (_face = mesh->findPoint(point, 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) const
{
- using Limits = std::numeric_limits<typename glm::vec2::value_type>;
- static_assert(Limits::has_infinity);
- if (d[axis] == 0) {
- return {0, Limits::infinity()};
+ return face(mesh, *mesh->faces_begin());
+}
+
+namespace {
+ template<template<typename> typename Op>
+ [[nodiscard]] constexpr inline auto
+ pointLineOp(const GlobalPosition2D p, const GlobalPosition2D e1, const GlobalPosition2D e2)
+ {
+ return Op {}(CalcDistance(e2.x - e1.x) * CalcDistance(p.y - e1.y),
+ CalcDistance(e2.y - e1.y) * CalcDistance(p.x - e1.x));
+ }
+
+ constexpr auto pointLeftOfLine = pointLineOp<std::greater>;
+ constexpr auto pointLeftOfOrOnLine = pointLineOp<std::greater_equal>;
+
+ 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}));
+ static_assert(pointLeftOfOrOnLine({310000000, 490000000}, {310000000, 490000000}, {310050000, 490050000}));
+ static_assert(pointLeftOfOrOnLine({310000000, 490000000}, {310050000, 490050000}, {310000000, 490050000}));
+ static_assert(pointLeftOfOrOnLine({310000000, 490000000}, {310000000, 490050000}, {310000000, 490000000}));
+
+ [[nodiscard]] constexpr inline bool
+ linesCross(
+ const GlobalPosition2D a1, const GlobalPosition2D a2, const GlobalPosition2D b1, const GlobalPosition2D b2)
+ {
+ return (pointLeftOfLine(a2, b1, b2) == pointLeftOfLine(a1, b2, b1))
+ && (pointLeftOfLine(b1, a1, a2) == pointLeftOfLine(b2, a2, a1));
}
- else if (p1[axis] > 0) {
- return {1, (std::floor(p0[axis]) + 1.F - p0[axis]) * d[1 - axis]};
+
+ static_assert(linesCross({1, 1}, {2, 2}, {1, 2}, {2, 1}));
+ static_assert(linesCross({2, 2}, {1, 1}, {1, 2}, {2, 1}));
+
+ [[nodiscard]] constexpr inline bool
+ linesCrossLtR(
+ const GlobalPosition2D a1, const GlobalPosition2D a2, const GlobalPosition2D b1, const GlobalPosition2D b2)
+ {
+ return pointLeftOfLine(a2, b1, b2) && pointLeftOfLine(a1, b2, b1) && pointLeftOfLine(b1, a1, a2)
+ && pointLeftOfLine(b2, a2, a1);
}
- else {
- return {-1, (p0[axis] - std::floor(p0[axis])) * d[1 - axis]};
+
+ static_assert(linesCrossLtR({1, 1}, {2, 2}, {1, 2}, {2, 1}));
+ static_assert(!linesCrossLtR({2, 2}, {1, 1}, {1, 2}, {2, 1}));
+
+ 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 = crossInt(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});
}
-glm::vec2
-GeoData::RayTracer::next()
+OpenMesh::FaceHandle
+GeoData::findPoint(GlobalPosition2D p, OpenMesh::FaceHandle f) const
{
- const glm::vec2 cur {p};
-
- 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];
+ 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;
+}
- return cur;
+GlobalPosition3D
+GeoData::positionAt(const PointFace & p) const
+{
+ return positionOnTriangle(p.point, triangle<3>(p.face(this)));
}
-std::optional<glm::vec3>
+[[nodiscard]] std::optional<GlobalPosition3D>
GeoData::intersectRay(const Ray & ray) 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]);
+ return intersectRay(ray, findPoint(ray.start));
+}
+
+[[nodiscard]] std::optional<GlobalPosition3D>
+GeoData::intersectRay(const Ray & ray, FaceHandle face) const
+{
+ std::optional<GlobalPosition3D> out;
+ walkUntil(PointFace {ray.start, face},
+ ray.start.xy() + (ray.direction.xy() * RelativePosition2D(upperExtent.xy() - lowerExtent.xy())),
+ [&out, &ray, this](FaceHandle face) {
+ BaryPosition bari {};
+ float dist {};
+ const auto t = triangle<3>(face);
+ if (glm::intersectRayTriangle<RelativePosition3D::value_type, glm::defaultp>(
+ ray.start, ray.direction, t[0], t[1], t[2], bari, dist)) {
+ out = t * bari;
+ return true;
}
- }
+ return false;
+ });
+ return out;
+}
+
+void
+GeoData::walk(const PointFace & from, const GlobalPosition2D to, const std::function<void(FaceHandle)> & op) const
+{
+ walkUntil(from, to, [&op](const auto & fh) {
+ op(fh);
+ return false;
+ });
+}
+
+void
+GeoData::walkUntil(const PointFace & from, const GlobalPosition2D to, const std::function<bool(FaceHandle)> & op) const
+{
+ auto f = from.face(this);
+ if (!f.is_valid()) {
+ const auto entryEdge = findEntry(from.point, to);
+ if (!entryEdge.is_valid()) {
+ return;
}
- catch (std::range_error &) {
- const auto rel = n / !ray.direction;
- if (rel.x > 0 && rel.y > 0) {
- return {};
+ f = opposite_face_handle(entryEdge);
+ }
+ 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 (linesCrossLtR(from.point, to, e1, e2)) {
+ previousFace = f;
+ break;
+ }
}
+ f.reset();
}
}
+}
+
+void
+GeoData::boundaryWalk(const std::function<void(HalfedgeHandle)> & op) const
+{
+ boundaryWalk(op, findBoundaryStart());
+}
+
+void
+GeoData::boundaryWalk(const std::function<void(HalfedgeHandle)> & op, HalfedgeHandle start) const
+{
+ assert(is_boundary(start));
+ boundaryWalkUntil(
+ [&op](auto heh) {
+ op(heh);
+ return false;
+ },
+ start);
+}
- return {};
+void
+GeoData::boundaryWalkUntil(const std::function<bool(HalfedgeHandle)> & op) const
+{
+ boundaryWalkUntil(op, findBoundaryStart());
}
-unsigned int
-GeoData::at(glm::ivec2 coord) const
+void
+GeoData::boundaryWalkUntil(const std::function<bool(HalfedgeHandle)> & op, HalfedgeHandle start) 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"};
+ assert(is_boundary(start));
+ if (!op(start)) {
+ for (auto heh = next_halfedge_handle(start); heh != start; heh = next_halfedge_handle(heh)) {
+ if (op(heh)) {
+ break;
+ }
+ }
}
- const glm::uvec2 offset = coord - limit.first;
- return offset.x + (offset.y * size.x);
}
-unsigned int
-GeoData::at(int x, int y) const
+GeoData::HalfedgeHandle
+GeoData::findEntry(const GlobalPosition2D from, const GlobalPosition2D to) const
{
- return at({x, y});
+ HalfedgeHandle entry;
+ boundaryWalkUntil([this, from, to, &entry](auto he) {
+ const auto e1 = point(to_vertex_handle(he));
+ const auto e2 = point(to_vertex_handle(opposite_halfedge_handle(he)));
+ if (linesCrossLtR(from, to, e1, e2)) {
+ entry = he;
+ return true;
+ }
+ return false;
+ });
+ return entry;
}
-GeoData::Limits
-GeoData::getLimit() const
+bool
+GeoData::triangleContainsPoint(const GlobalPosition2D p, const Triangle<2> & t)
{
- return limit;
+ return pointLeftOfOrOnLine(p, t[0], t[1]) && pointLeftOfOrOnLine(p, t[1], t[2])
+ && pointLeftOfOrOnLine(p, t[2], t[0]);
}
-float
-GeoData::getScale() const
+bool
+GeoData::triangleContainsPoint(const GlobalPosition2D p, FaceHandle face) const
{
- return scale;
+ return triangleContainsPoint(p, triangle<2>(face));
}
-glm::uvec2
-GeoData::getSize() const
+GeoData::HalfedgeHandle
+GeoData::findBoundaryStart() const
{
- return size;
+ return *std::find_if(halfedges_begin(), halfedges_end(), [this](const auto heh) {
+ return is_boundary(heh);
+ });
}
-std::span<const GeoData::Node>
-GeoData::getNodes() const
+void
+GeoData::update_vertex_normals_only()
{
- return nodes;
+ for (auto vh : all_vertices()) {
+ Normal3D n;
+ calc_vertex_normal_correct(vh, n);
+ this->set_normal(vh, glm::normalize(n));
+ }
}
diff --git a/game/geoData.h b/game/geoData.h
index f9a7d7b..d4d0fb3 100644
--- a/game/geoData.h
+++ b/game/geoData.h
@@ -1,62 +1,112 @@
#pragma once
-#include <array>
+#include "collections.h" // IWYU pragma: keep IterableCollection
+#include "config/types.h"
+#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::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:
- struct Node {
- float height {-1.5F};
- };
+class GeoData : public OpenMesh::TriMesh_ArrayKernelT<GeoDataTraits> {
+private:
+ GeoData() = default;
- using Quad = std::array<glm::vec3, 4>;
+public:
+ static GeoData loadFromAsciiGrid(const std::filesystem::path &);
+ static GeoData createFlat(GlobalPosition2D lower, GlobalPosition2D upper, GlobalDistance h);
- using Limits = std::pair<glm::ivec2, glm::ivec2>;
+ struct PointFace {
+ // NOLINTNEXTLINE(hicpp-explicit-conversions)
+ PointFace(const GlobalPosition2D p) : point {p} { }
- explicit GeoData(Limits limit, float scale = 10.F);
+ PointFace(const GlobalPosition2D p, FaceHandle face) : point {p}, _face {face} { }
- void generateRandom();
- void loadFromImages(const std::filesystem::path &, float scale);
+ PointFace(const GlobalPosition2D p, const GeoData *);
+ PointFace(const GlobalPosition2D p, const GeoData *, FaceHandle start);
- [[nodiscard]] glm::vec3 positionAt(glm::vec2) const;
- [[nodiscard]] std::optional<glm::vec3> intersectRay(const Ray &) const;
+ const GlobalPosition2D 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, GlobalDistance>> {
+ using base = glm::vec<3, glm::vec<Dim, GlobalDistance>>;
+ 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, GlobalDistance>
+ operator*(BaryPosition bari) const
+ {
+ const auto & t {*this};
+ return t[0] + GlobalPosition<Dim>(RelativePosition<Dim>(t[1] - t[0]) * bari.x)
+ + GlobalPosition<Dim>(RelativePosition<Dim>(t[2] - t[1]) * bari.y);
+ }
};
+ [[nodiscard]] FaceHandle findPoint(GlobalPosition2D) const;
+ [[nodiscard]] FaceHandle findPoint(GlobalPosition2D, FaceHandle start) const;
+
+ [[nodiscard]] GlobalPosition3D positionAt(const PointFace &) const;
+ [[nodiscard]] std::optional<GlobalPosition3D> intersectRay(const Ray &) const;
+ [[nodiscard]] std::optional<GlobalPosition3D> intersectRay(const Ray &, FaceHandle start) const;
+
+ void walk(const PointFace & from, const GlobalPosition2D to, const std::function<void(FaceHandle)> & op) const;
+ void walkUntil(const PointFace & from, const GlobalPosition2D to, const std::function<bool(FaceHandle)> & op) const;
+
+ void boundaryWalk(const std::function<void(HalfedgeHandle)> &) const;
+ void boundaryWalk(const std::function<void(HalfedgeHandle)> &, HalfedgeHandle start) const;
+ void boundaryWalkUntil(const std::function<bool(HalfedgeHandle)> &) const;
+ void boundaryWalkUntil(const std::function<bool(HalfedgeHandle)> &, HalfedgeHandle start) const;
+
+ [[nodiscard]] HalfedgeHandle findEntry(const GlobalPosition2D from, const GlobalPosition2D to) 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;
+ template<glm::length_t Dim>
+ [[nodiscard]] Triangle<Dim>
+ triangle(FaceHandle f) const
+ {
+ return {this, fv_range(f)};
+ }
+
+ [[nodiscard]] static bool triangleContainsPoint(const GlobalPosition2D, const Triangle<2> &);
+ [[nodiscard]] bool triangleContainsPoint(const GlobalPosition2D, FaceHandle) const;
+ [[nodiscard]] HalfedgeHandle findBoundaryStart() const;
+
+ void update_vertex_normals_only();
+
+private:
+ GlobalPosition3D lowerExtent {}, upperExtent {};
};
diff --git a/game/network/link.cpp b/game/network/link.cpp
index bb27a52..703a1ca 100644
--- a/game/network/link.cpp
+++ b/game/network/link.cpp
@@ -8,10 +8,10 @@
Link::Link(End a, End b, float l) : ends {{std::move(a), std::move(b)}}, length {l} { }
-LinkCurve::LinkCurve(glm::vec3 c, float r, Arc a) : centreBase {c}, radius {r}, arc {std::move(a)} { }
+LinkCurve::LinkCurve(Position3D c, float r, Arc a) : centreBase {c}, radius {r}, arc {std::move(a)} { }
bool
-operator<(const glm::vec3 & a, const glm::vec3 & b)
+operator<(const Position3D & a, const Position3D & b)
{
// NOLINTNEXTLINE(hicpp-use-nullptr,modernize-use-nullptr)
return std::tie(a.x, a.y, a.z) < std::tie(b.x, b.y, b.z);
@@ -46,9 +46,9 @@ LinkCurve::positionAt(float dist, unsigned char start) const
const auto es {std::make_pair(ends[start].node.get(), ends[1 - start].node.get())};
const auto as {std::make_pair(arc[start], arc[1 - start])};
const auto ang {as.first + ((as.second - as.first) * frac)};
- const auto relPos {!sincosf(ang) * radius};
+ const auto relPos {(sincosf(ang) || 0.F) * radius};
const auto relClimb {vehiclePositionOffset()
- + glm::vec3 {0, 0, es.first->pos.z - centreBase.z + ((es.second->pos.z - es.first->pos.z) * frac)}};
+ + Position3D {0, 0, es.first->pos.z - centreBase.z + ((es.second->pos.z - es.first->pos.z) * frac)}};
const auto pitch {vector_pitch({0, 0, (es.second->pos.z - es.first->pos.z) / length})};
return Location {relPos + relClimb + centreBase, {pitch, normalize(ang + dirOffset[start]), 0}};
}
@@ -60,14 +60,14 @@ LinkCurve::intersectRay(const Ray & ray) const
const auto & e1p {ends[1].node->pos};
const auto slength = round_frac(length / 2.F, 5.F);
const auto segs = std::round(15.F * slength / std::pow(radius, 0.7F));
- const auto step {glm::vec3 {arc_length(arc), e1p.z - e0p.z, slength} / segs};
+ const auto step {Position3D {arc_length(arc), e1p.z - e0p.z, slength} / segs};
const auto trans {glm::translate(centreBase)};
auto segCount = static_cast<std::size_t>(std::lround(segs)) + 1;
- std::vector<glm::vec3> points;
+ std::vector<Position3D> points;
points.reserve(segCount);
- for (glm::vec3 swing = {arc.first, centreBase.z - e0p.z, 0.F}; segCount; swing += step, --segCount) {
- const auto t {trans * glm::rotate(half_pi - swing.x, up) * glm::translate(glm::vec3 {radius, 0.F, swing.y})};
+ for (Position3D swing = {arc.first, centreBase.z - e0p.z, 0.F}; segCount; swing += step, --segCount) {
+ const auto t {trans * glm::rotate(half_pi - swing.x, up) * glm::translate(Position3D {radius, 0.F, swing.y})};
points.emplace_back(t * glm::vec4 {0, 0, 0, 1});
}
return ray.passesCloseToEdges(points, 1.F);
diff --git a/game/network/link.h b/game/network/link.h
index 0d66c42..78d3e91 100644
--- a/game/network/link.h
+++ b/game/network/link.h
@@ -17,12 +17,12 @@ class Ray;
// it has location
class Node : public StdTypeDefs<Node> {
public:
- explicit Node(glm::vec3 p) noexcept : pos(p) {};
+ explicit Node(Position3D p) noexcept : pos(p) {};
virtual ~Node() noexcept = default;
NO_COPY(Node);
NO_MOVE(Node);
- glm::vec3 pos;
+ Position3D pos;
};
// Generic network link
@@ -51,14 +51,14 @@ public:
float length;
protected:
- [[nodiscard]] virtual glm::vec3
+ [[nodiscard]] virtual Position3D
vehiclePositionOffset() const
{
return {};
}
};
-bool operator<(const glm::vec3 & a, const glm::vec3 & b);
+bool operator<(const Position3D & a, const Position3D & b);
bool operator<(const Node & a, const Node & b);
class LinkStraight : public virtual Link {
@@ -77,14 +77,14 @@ LinkStraight::~LinkStraight() = default;
class LinkCurve : public virtual Link {
public:
inline ~LinkCurve() override = 0;
- LinkCurve(glm::vec3, float, Arc);
+ LinkCurve(Position3D, float, Arc);
NO_COPY(LinkCurve);
NO_MOVE(LinkCurve);
[[nodiscard]] Location positionAt(float dist, unsigned char start) const override;
[[nodiscard]] bool intersectRay(const Ray &) const override;
- glm::vec3 centreBase;
+ Position3D centreBase;
float radius;
Arc arc;
};
diff --git a/game/network/network.cpp b/game/network/network.cpp
index 083b08e..1ff5b26 100644
--- a/game/network/network.cpp
+++ b/game/network/network.cpp
@@ -14,13 +14,13 @@
Network::Network(const std::string & tn) : texture {Texture::cachedTexture.get(tn)} { }
Node::Ptr
-Network::nodeAt(glm::vec3 pos)
+Network::nodeAt(Position3D pos)
{
return newNodeAt(pos).first;
}
Network::NodeInsertion
-Network::newNodeAt(glm::vec3 pos)
+Network::newNodeAt(Position3D pos)
{
if (auto [n, i] = candidateNodeAt(pos); i == NodeIs::NotInNetwork) {
return {*nodes.insert(std::move(n)).first, i};
@@ -31,7 +31,7 @@ Network::newNodeAt(glm::vec3 pos)
}
Node::Ptr
-Network::findNodeAt(glm::vec3 pos) const
+Network::findNodeAt(Position3D pos) const
{
if (const auto n = nodes.find(pos); n != nodes.end()) {
return *n;
@@ -40,7 +40,7 @@ Network::findNodeAt(glm::vec3 pos) const
}
Network::NodeInsertion
-Network::candidateNodeAt(glm::vec3 pos) const
+Network::candidateNodeAt(Position3D pos) const
{
if (const auto n = nodes.find(pos); n != nodes.end()) {
return {*n, NodeIs::InNetwork};
@@ -54,7 +54,7 @@ Network::intersectRayNodes(const Ray & ray) const
// Click within 2m of a node
if (const auto node = std::find_if(nodes.begin(), nodes.end(),
[&ray](const Node::Ptr & node) {
- glm::vec3 ipos, inorm;
+ Position3D ipos, inorm;
return glm::intersectRaySphere(ray.start, ray.direction, node->pos, 2.F, ipos, inorm);
});
node != nodes.end()) {
@@ -79,7 +79,7 @@ Network::joinLinks(const Link::Ptr & l, const Link::Ptr & ol)
}
Link::Nexts
-Network::routeFromTo(const Link::End & start, glm::vec3 dest) const
+Network::routeFromTo(const Link::End & start, Position3D dest) const
{
auto destNode {findNodeAt(dest)};
if (!destNode) {
@@ -95,12 +95,12 @@ Network::routeFromTo(const Link::End & end, const Node::Ptr & dest) const
}
GenCurveDef
-Network::genCurveDef(const glm::vec3 & start, const glm::vec3 & end, float startDir)
+Network::genCurveDef(const Position3D & start, const Position3D & end, float startDir)
{
const auto diff {end - start};
const auto vy {vector_yaw(diff)};
const auto dir = pi + startDir;
- const auto flatStart {!start}, flatEnd {!end};
+ const auto flatStart {start.xy()}, flatEnd {end.xy()};
const auto n2ed {(vy * 2) - dir - pi};
const auto centre {find_arc_centre(flatStart, dir, flatEnd, n2ed)};
@@ -111,11 +111,11 @@ Network::genCurveDef(const glm::vec3 & start, const glm::vec3 & end, float start
}
std::pair<GenCurveDef, GenCurveDef>
-Network::genCurveDef(const glm::vec3 & start, const glm::vec3 & end, float startDir, float endDir)
+Network::genCurveDef(const Position3D & start, const Position3D & end, float startDir, float endDir)
{
startDir += pi;
endDir += pi;
- const glm::vec2 flatStart {!start}, flatEnd {!end};
+ const Position2D flatStart {start.xy()}, flatEnd {end.xy()};
auto midheight = [&](auto mid) {
const auto sm = glm::distance(flatStart, mid), em = glm::distance(flatEnd, mid);
return start.z + ((end.z - start.z) * (sm / (sm + em)));
@@ -125,7 +125,7 @@ Network::genCurveDef(const glm::vec3 & start, const glm::vec3 & end, float start
const auto c1 = flatStart + sincosf(startDir + half_pi) * radius;
const auto c2 = flatEnd + sincosf(endDir + half_pi) * radius;
const auto mid = (c1 + c2) / 2.F;
- const auto midh = mid ^ midheight(mid);
+ const auto midh = mid || midheight(mid);
return {{start, midh, c1}, {end, midh, c2}};
}
else {
@@ -133,7 +133,7 @@ Network::genCurveDef(const glm::vec3 & start, const glm::vec3 & end, float start
const auto c1 = flatStart + sincosf(startDir - half_pi) * radius;
const auto c2 = flatEnd + sincosf(endDir - half_pi) * radius;
const auto mid = (c1 + c2) / 2.F;
- const auto midh = mid ^ midheight(mid);
+ const auto midh = mid || midheight(mid);
return {{midh, start, c1}, {midh, end, c2}};
}
}
diff --git a/game/network/network.h b/game/network/network.h
index b9316eb..8af06a9 100644
--- a/game/network/network.h
+++ b/game/network/network.h
@@ -16,7 +16,7 @@ class Texture;
class SceneShader;
class Ray;
-template<size_t... n> using GenDef = std::tuple<glm::vec<n, float>...>;
+template<size_t... n> using GenDef = std::tuple<glm::vec<n, Distance>...>;
using GenCurveDef = GenDef<3, 3, 2>;
class Network {
@@ -26,32 +26,32 @@ public:
virtual ~Network() = default;
DEFAULT_MOVE_NO_COPY(Network);
- [[nodiscard]] Node::Ptr findNodeAt(glm::vec3) const;
- [[nodiscard]] Node::Ptr nodeAt(glm::vec3);
+ [[nodiscard]] Node::Ptr findNodeAt(Position3D) const;
+ [[nodiscard]] Node::Ptr nodeAt(Position3D);
enum class NodeIs { InNetwork, NotInNetwork };
using NodeInsertion = std::pair<Node::Ptr, NodeIs>;
- [[nodiscard]] NodeInsertion newNodeAt(glm::vec3);
- [[nodiscard]] NodeInsertion candidateNodeAt(glm::vec3) const;
+ [[nodiscard]] NodeInsertion newNodeAt(Position3D);
+ [[nodiscard]] NodeInsertion candidateNodeAt(Position3D) const;
[[nodiscard]] virtual Link::Ptr intersectRayLinks(const Ray &) const = 0;
[[nodiscard]] virtual Node::Ptr intersectRayNodes(const Ray &) const;
- [[nodiscard]] Link::Nexts routeFromTo(const Link::End &, glm::vec3) const;
+ [[nodiscard]] Link::Nexts routeFromTo(const Link::End &, Position3D) const;
[[nodiscard]] Link::Nexts routeFromTo(const Link::End &, const Node::Ptr &) const;
- virtual Link::CCollection candidateStraight(glm::vec3, glm::vec3) = 0;
- virtual Link::CCollection candidateJoins(glm::vec3, glm::vec3) = 0;
- virtual Link::CCollection candidateExtend(glm::vec3, glm::vec3) = 0;
- virtual Link::CCollection addStraight(glm::vec3, glm::vec3) = 0;
- virtual Link::CCollection addJoins(glm::vec3, glm::vec3) = 0;
- virtual Link::CCollection addExtend(glm::vec3, glm::vec3) = 0;
+ virtual Link::CCollection candidateStraight(Position3D, Position3D) = 0;
+ virtual Link::CCollection candidateJoins(Position3D, Position3D) = 0;
+ virtual Link::CCollection candidateExtend(Position3D, Position3D) = 0;
+ virtual Link::CCollection addStraight(Position3D, Position3D) = 0;
+ virtual Link::CCollection addJoins(Position3D, Position3D) = 0;
+ virtual Link::CCollection addExtend(Position3D, Position3D) = 0;
[[nodiscard]] virtual float findNodeDirection(Node::AnyCPtr) const = 0;
protected:
static void joinLinks(const Link::Ptr & l, const Link::Ptr & ol);
- static GenCurveDef genCurveDef(const glm::vec3 & start, const glm::vec3 & end, float startDir);
+ static GenCurveDef genCurveDef(const Position3D & start, const Position3D & end, float startDir);
static std::pair<GenCurveDef, GenCurveDef> genCurveDef(
- const glm::vec3 & start, const glm::vec3 & end, float startDir, float endDir);
+ const Position3D & start, const Position3D & end, float startDir, float endDir);
using Nodes = std::set<Node::Ptr, PtrMemberSorter<Node::Ptr, &Node::pos>>;
Nodes nodes;
@@ -71,7 +71,7 @@ protected:
public:
template<typename L, typename... Params>
std::shared_ptr<L>
- candidateLink(glm::vec3 a, glm::vec3 b, Params &&... params)
+ candidateLink(Position3D a, Position3D b, Params &&... params)
requires std::is_base_of_v<T, L>
{
const auto node1 = candidateNodeAt(a).first, node2 = candidateNodeAt(b).first;
@@ -80,7 +80,7 @@ public:
template<typename L, typename... Params>
std::shared_ptr<L>
- addLink(glm::vec3 a, glm::vec3 b, Params &&... params)
+ addLink(Position3D a, Position3D b, Params &&... params)
requires std::is_base_of_v<T, L>
{
const auto node1 = nodeAt(a), node2 = nodeAt(b);
@@ -89,12 +89,12 @@ public:
return l;
}
- Link::CCollection candidateStraight(glm::vec3 n1, glm::vec3 n2) override;
- Link::CCollection candidateJoins(glm::vec3, glm::vec3) override;
- Link::CCollection candidateExtend(glm::vec3, glm::vec3) override;
- Link::CCollection addStraight(glm::vec3 n1, glm::vec3 n2) override;
- Link::CCollection addJoins(glm::vec3, glm::vec3) override;
- Link::CCollection addExtend(glm::vec3, glm::vec3) override;
+ Link::CCollection candidateStraight(Position3D n1, Position3D n2) override;
+ Link::CCollection candidateJoins(Position3D, Position3D) override;
+ Link::CCollection candidateExtend(Position3D, Position3D) override;
+ Link::CCollection addStraight(Position3D n1, Position3D n2) override;
+ Link::CCollection addJoins(Position3D, Position3D) override;
+ Link::CCollection addExtend(Position3D, Position3D) override;
[[nodiscard]] float findNodeDirection(Node::AnyCPtr) const override;
diff --git a/game/network/network.impl.h b/game/network/network.impl.h
index 045335f..8e9e85c 100644
--- a/game/network/network.impl.h
+++ b/game/network/network.impl.h
@@ -54,14 +54,14 @@ NetworkOf<T>::findNodeDirection(Node::AnyCPtr n) const
template<typename T>
Link::CCollection
-NetworkOf<T>::candidateStraight(glm::vec3 n1, glm::vec3 n2)
+NetworkOf<T>::candidateStraight(Position3D n1, Position3D n2)
{
return {candidateLink<typename T::StraightLink>(n1, n2)};
}
template<typename T>
Link::CCollection
-NetworkOf<T>::candidateJoins(glm::vec3 start, glm::vec3 end)
+NetworkOf<T>::candidateJoins(Position3D start, Position3D end)
{
if (glm::distance(start, end) < 2.F) {
return {};
@@ -75,7 +75,7 @@ NetworkOf<T>::candidateJoins(glm::vec3 start, glm::vec3 end)
template<typename T>
Link::CCollection
-NetworkOf<T>::candidateExtend(glm::vec3 start, glm::vec3 end)
+NetworkOf<T>::candidateExtend(Position3D start, Position3D end)
{
const auto [cstart, cend, centre] = genCurveDef(start, end, findNodeDirection(candidateNodeAt(start).first));
return {candidateLink<typename T::CurveLink>(cstart, cend, centre)};
@@ -83,14 +83,14 @@ NetworkOf<T>::candidateExtend(glm::vec3 start, glm::vec3 end)
template<typename T>
Link::CCollection
-NetworkOf<T>::addStraight(glm::vec3 n1, glm::vec3 n2)
+NetworkOf<T>::addStraight(Position3D n1, Position3D n2)
{
return {addLink<typename T::StraightLink>(n1, n2)};
}
template<typename T>
Link::CCollection
-NetworkOf<T>::addJoins(glm::vec3 start, glm::vec3 end)
+NetworkOf<T>::addJoins(Position3D start, Position3D end)
{
if (glm::distance(start, end) < 2.F) {
return {};
@@ -103,7 +103,7 @@ NetworkOf<T>::addJoins(glm::vec3 start, glm::vec3 end)
template<typename T>
Link::CCollection
-NetworkOf<T>::addExtend(glm::vec3 start, glm::vec3 end)
+NetworkOf<T>::addExtend(Position3D start, Position3D end)
{
const auto [cstart, cend, centre] = genCurveDef(start, end, findNodeDirection(nodeAt(start)));
return {addLink<typename T::CurveLink>(cstart, cend, centre)};
diff --git a/game/network/rail.cpp b/game/network/rail.cpp
index 5a4f1e1..303f1c8 100644
--- a/game/network/rail.cpp
+++ b/game/network/rail.cpp
@@ -18,7 +18,7 @@
template class NetworkOf<RailLink>;
constexpr auto RAIL_CROSSSECTION_VERTICES {5U};
-constexpr glm::vec3 RAIL_HEIGHT {0, 0, .25F};
+constexpr Size3D RAIL_HEIGHT {0, 0, 250.F};
RailLinks::RailLinks() : NetworkOf<RailLink> {"rails.jpg"} { }
@@ -28,7 +28,7 @@ RailLinks::tick(TickDuration)
}
std::shared_ptr<RailLink>
-RailLinks::addLinksBetween(glm::vec3 start, glm::vec3 end)
+RailLinks::addLinksBetween(Position3D start, Position3D end)
{
auto node1ins = newNodeAt(start), node2ins = newNodeAt(end);
if (node1ins.second == NodeIs::NotInNetwork && node2ins.second == NodeIs::NotInNetwork) {
@@ -45,7 +45,7 @@ RailLinks::addLinksBetween(glm::vec3 start, glm::vec3 end)
if (dir == vector_yaw(end - start)) {
return addLink<RailLinkStraight>(start, end);
}
- const glm::vec2 flatStart {!start}, flatEnd {!end};
+ const Position2D flatStart {start.xy()}, flatEnd {end.xy()};
if (node2ins.second == NodeIs::InNetwork) {
auto midheight = [&](auto mid) {
const auto sm = glm::distance(flatStart, mid), em = glm::distance(flatEnd, mid);
@@ -57,7 +57,7 @@ RailLinks::addLinksBetween(glm::vec3 start, glm::vec3 end)
const auto c1 = flatStart + sincosf(dir + half_pi) * radius;
const auto c2 = flatEnd + sincosf(dir2 + half_pi) * radius;
const auto mid = (c1 + c2) / 2.F;
- const auto midh = mid ^ midheight(mid);
+ const auto midh = mid || midheight(mid);
addLink<RailLinkCurve>(start, midh, c1);
return addLink<RailLinkCurve>(end, midh, c2);
}
@@ -66,7 +66,7 @@ RailLinks::addLinksBetween(glm::vec3 start, glm::vec3 end)
const auto c1 = flatStart + sincosf(dir - half_pi) * radius;
const auto c2 = flatEnd + sincosf(dir2 - half_pi) * radius;
const auto mid = (c1 + c2) / 2.F;
- const auto midh = mid ^ midheight(mid);
+ const auto midh = mid || midheight(mid);
addLink<RailLinkCurve>(midh, start, c1);
return addLink<RailLinkCurve>(midh, end, c2);
}
@@ -100,15 +100,15 @@ RailLink::render(const SceneShader &) const
mesh->Draw();
}
-constexpr const std::array<std::pair<glm::vec3, float>, RAIL_CROSSSECTION_VERTICES> railCrossSection {{
+constexpr const std::array<std::pair<Position3D, float>, RAIL_CROSSSECTION_VERTICES> railCrossSection {{
// ___________
// _/ \_
// left to right
- {{-1.9F, 0.F, 0.F}, 0.F},
- {{-.608F, 0.F, RAIL_HEIGHT.z}, 0.34F},
- {{0, 0.F, RAIL_HEIGHT.z * .7F}, 0.5F},
- {{.608F, 0.F, RAIL_HEIGHT.z}, 0.66F},
- {{1.9F, 0.F, 0.F}, 1.F},
+ {{-1900.F, 0.F, 0.F}, 0.F},
+ {{-608.F, 0.F, RAIL_HEIGHT.z}, .34F},
+ {{0, 0.F, RAIL_HEIGHT.z * .7F}, .5F},
+ {{608.F, 0.F, RAIL_HEIGHT.z}, .66F},
+ {{1900.F, 0.F, 0.F}, 1.F},
}};
constexpr auto sleepers {5.F}; // There are 5 repetitions of sleepers in the texture
@@ -122,31 +122,31 @@ RailLinkStraight::RailLinkStraight(const Node::Ptr & a, const Node::Ptr & b) : R
{
}
-RailLinkStraight::RailLinkStraight(Node::Ptr a, Node::Ptr b, const glm::vec3 & diff) :
+RailLinkStraight::RailLinkStraight(Node::Ptr a, Node::Ptr b, const Position3D & diff) :
Link({std::move(a), vector_yaw(diff)}, {std::move(b), vector_yaw(-diff)}, glm::length(diff))
{
if (glGenVertexArrays) {
std::vector<Vertex> vertices;
vertices.reserve(2 * railCrossSection.size());
- const auto len = round_sleepers(length / 2.F);
+ const auto len = round_sleepers(length / 2000.F);
const auto e {flat_orientation(diff)};
for (auto ei : {1U, 0U}) {
const auto trans {glm::translate(ends[ei].node->pos) * e};
for (const auto & rcs : railCrossSection) {
- const glm::vec3 m {(trans * glm::vec4 {rcs.first, 1})};
- vertices.emplace_back(m, glm::vec2 {rcs.second, len * static_cast<float>(ei)}, up);
+ const Position3D m {(trans * (rcs.first || 1.F))};
+ vertices.emplace_back(m, Position2D {rcs.second, len * static_cast<float>(ei)}, up);
}
}
mesh = defaultMesh(vertices);
}
}
-RailLinkCurve::RailLinkCurve(const Node::Ptr & a, const Node::Ptr & b, glm::vec2 c) :
- RailLinkCurve(a, b, c ^ a->pos.z, {!c, a->pos, b->pos})
+RailLinkCurve::RailLinkCurve(const Node::Ptr & a, const Node::Ptr & b, Position2D c) :
+ RailLinkCurve(a, b, c || a->pos.z, {c || 0.F, a->pos, b->pos})
{
}
-RailLinkCurve::RailLinkCurve(const Node::Ptr & a, const Node::Ptr & b, glm::vec3 c, const Arc arc) :
+RailLinkCurve::RailLinkCurve(const Node::Ptr & a, const Node::Ptr & b, Position3D c, const Arc arc) :
Link({a, normalize(arc.first + half_pi)}, {b, normalize(arc.second - half_pi)},
(glm::length(a->pos - c)) * arc_length(arc)),
LinkCurve {c, glm::length(ends[0].node->pos - c), arc}
@@ -155,26 +155,26 @@ RailLinkCurve::RailLinkCurve(const Node::Ptr & a, const Node::Ptr & b, glm::vec3
const auto & e0p {ends[0].node->pos};
const auto & e1p {ends[1].node->pos};
const auto slength = round_sleepers(length / 2.F);
- const auto segs = std::round(15.F * slength / std::pow(radius, 0.7F));
- const auto step {glm::vec3 {arc_length(arc), e1p.z - e0p.z, slength} / segs};
+ const auto segs = std::round(slength / std::pow(radius, 0.7F));
+ const auto step {Position3D {arc_length(arc), e1p.z - e0p.z, slength / 1000.F} / segs};
const auto trans {glm::translate(centreBase)};
auto segCount = static_cast<std::size_t>(std::lround(segs)) + 1;
std::vector<Vertex> vertices;
vertices.reserve(segCount * railCrossSection.size());
- for (glm::vec3 swing = {arc.first, centreBase.z - e0p.z, 0.F}; segCount; swing += step, --segCount) {
+ for (Position3D swing = {arc.first, centreBase.z - e0p.z, 0.F}; segCount; swing += step, --segCount) {
const auto t {
- trans * glm::rotate(half_pi - swing.x, up) * glm::translate(glm::vec3 {radius, 0.F, swing.y})};
+ trans * glm::rotate(half_pi - swing.x, up) * glm::translate(Position3D {radius, 0.F, swing.y})};
for (const auto & rcs : railCrossSection) {
- const glm::vec3 m {(t * glm::vec4 {rcs.first, 1})};
- vertices.emplace_back(m, glm::vec2 {rcs.second, swing.z}, up);
+ const Position3D m {(t * (rcs.first || 1.F))};
+ vertices.emplace_back(m, Position2D {rcs.second, swing.z}, up);
}
}
mesh = defaultMesh(vertices);
}
}
-glm::vec3
+Position3D
RailLink::vehiclePositionOffset() const
{
return RAIL_HEIGHT;
diff --git a/game/network/rail.h b/game/network/rail.h
index 6684801..4a1932f 100644
--- a/game/network/rail.h
+++ b/game/network/rail.h
@@ -32,7 +32,7 @@ public:
NO_MOVE(RailLink);
protected:
- [[nodiscard]] glm::vec3 vehiclePositionOffset() const override;
+ [[nodiscard]] Position3D vehiclePositionOffset() const override;
[[nodiscard]] static Mesh::Ptr defaultMesh(const std::span<Vertex> vertices);
Mesh::Ptr mesh;
@@ -45,22 +45,22 @@ public:
RailLinkStraight(const Node::Ptr &, const Node::Ptr &);
private:
- RailLinkStraight(Node::Ptr, Node::Ptr, const glm::vec3 & diff);
+ RailLinkStraight(Node::Ptr, Node::Ptr, const Position3D & diff);
};
class RailLinkCurve : public RailLink, public LinkCurve {
public:
- RailLinkCurve(const Node::Ptr &, const Node::Ptr &, glm::vec2);
+ RailLinkCurve(const Node::Ptr &, const Node::Ptr &, Position2D);
private:
- RailLinkCurve(const Node::Ptr &, const Node::Ptr &, glm::vec3, const Arc);
+ RailLinkCurve(const Node::Ptr &, const Node::Ptr &, Position3D, const Arc);
};
class RailLinks : public NetworkOf<RailLink>, public WorldObject {
public:
RailLinks();
- std::shared_ptr<RailLink> addLinksBetween(glm::vec3 start, glm::vec3 end);
+ std::shared_ptr<RailLink> addLinksBetween(Position3D start, Position3D end);
private:
void tick(TickDuration elapsed) override;
diff --git a/game/scenary/foliage.cpp b/game/scenary/foliage.cpp
index 702a52c..c258b77 100644
--- a/game/scenary/foliage.cpp
+++ b/game/scenary/foliage.cpp
@@ -15,7 +15,8 @@ void
Foliage::postLoad()
{
texture = getTexture();
- bodyMesh->configureVAO(instanceVAO).addAttribs<glm::mat4>(instances.bufferName(), 1);
+ bodyMesh->configureVAO(instanceVAO)
+ .addAttribs<LocationVertex, &LocationVertex::first, &LocationVertex::second>(instances.bufferName(), 1);
}
void
diff --git a/game/scenary/foliage.h b/game/scenary/foliage.h
index b72a9c2..bbb6200 100644
--- a/game/scenary/foliage.h
+++ b/game/scenary/foliage.h
@@ -15,7 +15,8 @@ class Foliage : public Asset, public Renderable, public StdTypeDefs<Foliage> {
glVertexArray instanceVAO;
public:
- mutable InstanceVertices<glm::mat4> instances;
+ using LocationVertex = std::pair<glm::mat4, GlobalPosition3D>;
+ mutable InstanceVertices<LocationVertex> instances;
void render(const SceneShader &) const override;
void shadows(const ShadowMapper &) const override;
diff --git a/game/scenary/plant.cpp b/game/scenary/plant.cpp
index 4fb3cb5..b39c28b 100644
--- a/game/scenary/plant.cpp
+++ b/game/scenary/plant.cpp
@@ -2,6 +2,6 @@
#include "location.h"
Plant::Plant(std::shared_ptr<const Foliage> type, const Location & position) :
- type {std::move(type)}, location {this->type->instances.acquire(position.getTransform())}
+ type {std::move(type)}, location {this->type->instances.acquire(position.getRotationTransform(), position.pos)}
{
}
diff --git a/game/scenary/plant.h b/game/scenary/plant.h
index 82ab0e5..77c9ff7 100644
--- a/game/scenary/plant.h
+++ b/game/scenary/plant.h
@@ -7,7 +7,7 @@ class Location;
class Plant : public WorldObject {
std::shared_ptr<const Foliage> type;
- InstanceVertices<glm::mat4>::InstanceProxy location;
+ InstanceVertices<Foliage::LocationVertex>::InstanceProxy location;
void
tick(TickDuration) override
diff --git a/game/selectable.h b/game/selectable.h
index 31287d8..9732dca 100644
--- a/game/selectable.h
+++ b/game/selectable.h
@@ -1,5 +1,6 @@
#pragma once
+#include "config/types.h"
#include <glm/glm.hpp>
#include <special_members.h>
@@ -11,5 +12,5 @@ public:
virtual ~Selectable() = default;
DEFAULT_MOVE_COPY(Selectable);
- [[nodiscard]] virtual bool intersectRay(const Ray &, glm::vec2 *, float *) const = 0;
+ [[nodiscard]] virtual bool intersectRay(const Ray &, Position2D *, float *) const = 0;
};
diff --git a/game/terrain.cpp b/game/terrain.cpp
index f3bec1e..3778f3d 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 auto p = geoData->point(v);
+ return Vertex {p, p / 10000, 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/vehicles/linkHistory.cpp b/game/vehicles/linkHistory.cpp
index 2802109..e6bab36 100644
--- a/game/vehicles/linkHistory.cpp
+++ b/game/vehicles/linkHistory.cpp
@@ -8,7 +8,7 @@ LinkHistory::add(const Link::WPtr & l, unsigned char d)
links.insert(links.begin(), {l, d});
const auto lp = l.lock();
totalLen += lp->length;
- while (totalLen >= 1000.F && !links.empty()) {
+ while (totalLen >= 1000000.F && !links.empty()) {
totalLen -= links.back().first.lock()->length;
links.pop_back();
}
diff --git a/game/vehicles/railVehicle.cpp b/game/vehicles/railVehicle.cpp
index 2d820b6..7e4b1ee 100644
--- a/game/vehicles/railVehicle.cpp
+++ b/game/vehicles/railVehicle.cpp
@@ -12,10 +12,22 @@
#include <ray.h>
RailVehicle::RailVehicle(RailVehicleClassPtr rvc) :
- RailVehicleClass::Instance {rvc->instances.acquire()}, rvClass {std::move(rvc)}, location {&LV::body, *this},
+ RailVehicleClass::Instance {rvc->instances.acquire()}, rvClass {std::move(rvc)},
+ location {[this](const BufferedLocation * l) {
+ this->get()->body = l->getRotationTransform();
+ this->get()->bodyPos = l->position();
+ }},
bogies {{
- {&LV::front, *this, glm::vec3 {0, rvClass->wheelBase / 2.F, 0}},
- {&LV::back, *this, glm::vec3 {0, -rvClass->wheelBase / 2.F, 0}},
+ {[this](const BufferedLocation * l) {
+ this->get()->front = l->getRotationTransform();
+ this->get()->frontPos = l->position();
+ },
+ Position3D {0, rvClass->wheelBase / 2.F, 0}},
+ {[this](const BufferedLocation * l) {
+ this->get()->back = l->getRotationTransform();
+ this->get()->backPos = l->position();
+ },
+ Position3D {0, -rvClass->wheelBase / 2.F, 0}},
}}
{
}
@@ -26,29 +38,29 @@ RailVehicle::move(const Train * t, float & trailBy)
const auto overhang {(rvClass->length - rvClass->wheelBase) / 2};
const auto & b1Pos = bogies[0] = t->getBogiePosition(t->linkDist, trailBy += overhang);
const auto & b2Pos = bogies[1] = t->getBogiePosition(t->linkDist, trailBy += rvClass->wheelBase);
- const auto diff = glm::normalize(b2Pos.position() - b1Pos.position());
- location.setLocation((b1Pos.position() + b2Pos.position()) / 2.F, {vector_pitch(diff), vector_yaw(diff), 0});
- trailBy += 0.6F + overhang;
+ const auto diff = glm::normalize(RelativePosition3D(b2Pos.position() - b1Pos.position()));
+ location.setLocation((b1Pos.position() + b2Pos.position()) / 2, {vector_pitch(diff), vector_yaw(diff), 0});
+ trailBy += 600.F + overhang;
}
bool
-RailVehicle::intersectRay(const Ray & ray, glm::vec2 * baryPos, float * distance) const
+RailVehicle::intersectRay(const Ray & ray, BaryPosition * baryPos, float * distance) const
{
- constexpr const auto X = 1.35F;
+ constexpr const auto X = 1350.F;
const auto Y = this->rvClass->length / 2.F;
- constexpr const auto Z = 3.9F;
- const auto moveBy = location.getTransform();
- const std::array<glm::vec3, 8> cornerVertices {{
- moveBy * glm::vec4 {-X, Y, 0, 1}, // LFB
- moveBy * glm::vec4 {X, Y, 0, 1}, // RFB
- moveBy * glm::vec4 {-X, Y, Z, 1}, // LFT
- moveBy * glm::vec4 {X, Y, Z, 1}, // RFT
- moveBy * glm::vec4 {-X, -Y, 0, 1}, // LBB
- moveBy * glm::vec4 {X, -Y, 0, 1}, // RBB
- moveBy * glm::vec4 {-X, -Y, Z, 1}, // LBT
- moveBy * glm::vec4 {X, -Y, Z, 1}, // RBT
+ constexpr const auto Z = 3900.F;
+ const auto moveBy = location.getRotationTransform();
+ const std::array<Position3D, 8> cornerVertices {{
+ location.position() + GlobalPosition3D(moveBy * glm::vec4 {-X, Y, 0, 1}).xyz(), // LFB
+ location.position() + GlobalPosition3D(moveBy * glm::vec4 {X, Y, 0, 1}).xyz(), // RFB
+ location.position() + GlobalPosition3D(moveBy * glm::vec4 {-X, Y, Z, 1}).xyz(), // LFT
+ location.position() + GlobalPosition3D(moveBy * glm::vec4 {X, Y, Z, 1}).xyz(), // RFT
+ location.position() + GlobalPosition3D(moveBy * glm::vec4 {-X, -Y, 0, 1}).xyz(), // LBB
+ location.position() + GlobalPosition3D(moveBy * glm::vec4 {X, -Y, 0, 1}).xyz(), // RBB
+ location.position() + GlobalPosition3D(moveBy * glm::vec4 {-X, -Y, Z, 1}).xyz(), // LBT
+ location.position() + GlobalPosition3D(moveBy * glm::vec4 {X, -Y, Z, 1}).xyz(), // RBT
}};
- static constexpr const std::array<glm::uvec3, 10> triangles {{
+ static constexpr const std::array<glm::vec<3, uint8_t>, 10> triangles {{
// Front
{0, 1, 2},
{1, 2, 3},
@@ -66,7 +78,7 @@ RailVehicle::intersectRay(const Ray & ray, glm::vec2 * baryPos, float * distance
{3, 6, 7},
}};
return std::any_of(
- triangles.begin(), triangles.end(), [&cornerVertices, &ray, &baryPos, &distance](const glm::uvec3 idx) {
+ triangles.begin(), triangles.end(), [&cornerVertices, &ray, &baryPos, &distance](const auto & idx) {
return glm::intersectRayTriangle(ray.start, ray.direction, cornerVertices[idx[0]],
cornerVertices[idx[1]], cornerVertices[idx[2]], *baryPos, *distance);
});
diff --git a/game/vehicles/railVehicle.h b/game/vehicles/railVehicle.h
index be88142..8cbc49d 100644
--- a/game/vehicles/railVehicle.h
+++ b/game/vehicles/railVehicle.h
@@ -17,13 +17,12 @@ public:
void move(const Train *, float & trailBy);
- [[nodiscard]] bool intersectRay(const Ray &, glm::vec2 *, float *) const override;
+ [[nodiscard]] bool intersectRay(const Ray &, BaryPosition *, float *) const override;
RailVehicleClassPtr rvClass;
using LV = RailVehicleClass::LocationVertex;
- using BLocation = BufferedLocationT<glm::mat4 LV::*, RailVehicleClass::Instance &>;
- BLocation location;
- std::array<BLocation, 2> bogies;
+ BufferedLocationUpdater location;
+ std::array<BufferedLocationUpdater, 2> bogies;
};
using RailVehiclePtr = std::unique_ptr<RailVehicle>;
diff --git a/game/vehicles/railVehicleClass.cpp b/game/vehicles/railVehicleClass.cpp
index 324148c..3ca42db 100644
--- a/game/vehicles/railVehicleClass.cpp
+++ b/game/vehicles/railVehicleClass.cpp
@@ -35,13 +35,15 @@ void
RailVehicleClass::postLoad()
{
texture = getTexture();
- bodyMesh->configureVAO(instanceVAO).addAttribs<LocationVertex, &LocationVertex::body>(instances.bufferName(), 1);
+ bodyMesh->configureVAO(instanceVAO)
+ .addAttribs<LocationVertex, &LocationVertex::body, &LocationVertex::bodyPos>(instances.bufferName(), 1);
bogies.front()
->configureVAO(instancesBogiesVAO.front())
- .addAttribs<LocationVertex, &LocationVertex::front>(instances.bufferName(), 1);
+ .addAttribs<LocationVertex, &LocationVertex::front, &LocationVertex::frontPos>(instances.bufferName(), 1);
bogies.back()
->configureVAO(instancesBogiesVAO.back())
- .addAttribs<LocationVertex, &LocationVertex::back>(instances.bufferName(), 1);
+ .addAttribs<LocationVertex, &LocationVertex::back, &LocationVertex::backPos>(instances.bufferName(), 1);
+ static_assert(sizeof(LocationVertex) == 228UL);
}
void
@@ -64,7 +66,7 @@ RailVehicleClass::shadows(const ShadowMapper & mapper) const
if (const auto count = static_cast<GLsizei>(instances.size())) {
mapper.dynamicPointInst.use();
bodyMesh->DrawInstanced(instanceVAO, count);
- bogies.front()->DrawInstanced(instanceVAO, count);
- bogies.back()->DrawInstanced(instanceVAO, count);
+ bogies.front()->DrawInstanced(instancesBogiesVAO.front(), count);
+ bogies.back()->DrawInstanced(instancesBogiesVAO.back(), count);
}
}
diff --git a/game/vehicles/railVehicleClass.h b/game/vehicles/railVehicleClass.h
index 4668d7d..913feea 100644
--- a/game/vehicles/railVehicleClass.h
+++ b/game/vehicles/railVehicleClass.h
@@ -20,6 +20,7 @@ public:
struct LocationVertex {
glm::mat4 body, front, back;
+ GlobalPosition3D bodyPos, frontPos, backPos;
};
std::array<Mesh::Ptr, 2> bogies;
diff --git a/game/vehicles/train.cpp b/game/vehicles/train.cpp
index 6f3b036..13905a3 100644
--- a/game/vehicles/train.cpp
+++ b/game/vehicles/train.cpp
@@ -20,7 +20,7 @@ Train::getBogiePosition(float linkDist, float dist) const
}
bool
-Train::intersectRay(const Ray & ray, glm::vec2 * baryPos, float * distance) const
+Train::intersectRay(const Ray & ray, BaryPosition * baryPos, float * distance) const
{
return applyOne(&RailVehicle::intersectRay, ray, baryPos, distance) != end();
}
diff --git a/game/vehicles/train.h b/game/vehicles/train.h
index 20c3bc4..c77cd23 100644
--- a/game/vehicles/train.h
+++ b/game/vehicles/train.h
@@ -27,7 +27,7 @@ public:
return objects.front()->location;
}
- [[nodiscard]] bool intersectRay(const Ray &, glm::vec2 *, float *) const override;
+ [[nodiscard]] bool intersectRay(const Ray &, BaryPosition *, float *) const override;
void tick(TickDuration elapsed) override;
void doActivity(Go *, TickDuration) override;