summaryrefslogtreecommitdiff
path: root/game
diff options
context:
space:
mode:
Diffstat (limited to 'game')
-rw-r--r--game/geoData.cpp101
-rw-r--r--game/geoData.h28
-rw-r--r--game/network/network.cpp8
-rw-r--r--game/network/rail.cpp57
-rw-r--r--game/terrain.cpp4
5 files changed, 135 insertions, 63 deletions
diff --git a/game/geoData.cpp b/game/geoData.cpp
index d8caff7..f0e38d0 100644
--- a/game/geoData.cpp
+++ b/game/geoData.cpp
@@ -236,12 +236,12 @@ GeoData::intersectRay(const Ray<GlobalPosition3D> & ray, FaceHandle face) const
GeoData::IntersectionResult out;
walkUntil(PointFace {ray.start, face},
ray.start.xy() + (ray.direction.xy() * RelativePosition2D(upperExtent.xy() - lowerExtent.xy())),
- [&out, &ray, this](FaceHandle face) {
+ [&out, &ray, this](const auto & step) {
BaryPosition bari {};
RelativeDistance dist {};
- const auto t = triangle<3>(face);
+ const auto t = triangle<3>(step.current);
if (ray.intersectTriangle(t.x, t.y, t.z, bari, dist)) {
- out.emplace(t * bari, face);
+ out.emplace(t * bari, step.current);
return true;
}
return false;
@@ -250,7 +250,7 @@ GeoData::intersectRay(const Ray<GlobalPosition3D> & ray, FaceHandle face) const
}
void
-GeoData::walk(const PointFace & from, const GlobalPosition2D to, const std::function<void(FaceHandle)> & op) const
+GeoData::walk(const PointFace & from, const GlobalPosition2D to, Consumer<WalkStep> op) const
{
walkUntil(from, to, [&op](const auto & fh) {
op(fh);
@@ -259,41 +259,86 @@ GeoData::walk(const PointFace & from, const GlobalPosition2D to, const std::func
}
void
-GeoData::walkUntil(const PointFace & from, const GlobalPosition2D to, const std::function<bool(FaceHandle)> & op) const
+GeoData::walkUntil(const PointFace & from, const GlobalPosition2D to, Tester<WalkStep> op) const
{
- auto f = from.face(this);
- if (!f.is_valid()) {
+ WalkStep step {
+ .current = from.face(this),
+ };
+ if (!step.current.is_valid()) {
const auto entryEdge = findEntry(from.point, to);
if (!entryEdge.is_valid()) {
return;
}
- f = opposite_face_handle(entryEdge);
+ step.current = 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)));
+ while (step.current.is_valid() && !op(step)) {
+ step.previous = step.current;
+ for (const auto next : fh_range(step.current)) {
+ step.current = opposite_face_handle(next);
+ if (step.current.is_valid() && step.current != step.previous) {
+ const auto e1 = point(to_vertex_handle(next));
+ const auto e2 = point(to_vertex_handle(opposite_halfedge_handle(next)));
if (linesCrossLtR(from.point, to, e1, e2)) {
- previousFace = f;
+ step.exitHalfedge = next;
+ step.exitPosition = linesIntersectAt(from.point.xy(), to.xy(), e1.xy(), e2.xy()).value();
break;
}
}
- f.reset();
+ step.current.reset();
}
}
}
void
-GeoData::boundaryWalk(const std::function<void(HalfedgeHandle)> & op) const
+GeoData::walk(const PointFace & from, GlobalPosition2D to, GlobalPosition2D centre, Consumer<WalkStep> op) const
+{
+ walkUntil(from, to, centre, [&op](const auto & fh) {
+ op(fh);
+ return false;
+ });
+}
+
+void
+GeoData::walkUntil(const PointFace & from, GlobalPosition2D to, GlobalPosition2D centre, Tester<WalkStep> op) const
+{
+ WalkStep step {
+ .current = from.face(this),
+ };
+ if (!step.current.is_valid()) {
+ const auto entryEdge = findEntry(from.point, to);
+ if (!entryEdge.is_valid()) {
+ return;
+ }
+ step.current = opposite_face_handle(entryEdge);
+ }
+ ArcSegment arc {centre, from.point, to};
+ while (step.current.is_valid() && !op(step)) {
+ step.previous = step.current;
+ for (const auto next : fh_range(step.current)) {
+ step.current = opposite_face_handle(next);
+ if (step.current.is_valid()) {
+ const auto e1 = point(to_vertex_handle(next));
+ const auto e2 = point(to_vertex_handle(opposite_halfedge_handle(next)));
+ if (const auto intersect = arc.crossesLineAt(e1, e2)) {
+ step.exitHalfedge = next;
+ arc.ep0 = step.exitPosition = intersect.value().first;
+ arc.first = std::nextafter(intersect.value().second, INFINITY);
+ break;
+ }
+ }
+ step.current.reset();
+ }
+ }
+}
+
+void
+GeoData::boundaryWalk(Consumer<HalfedgeHandle> op) const
{
boundaryWalk(op, findBoundaryStart());
}
void
-GeoData::boundaryWalk(const std::function<void(HalfedgeHandle)> & op, HalfedgeHandle start) const
+GeoData::boundaryWalk(Consumer<HalfedgeHandle> op, HalfedgeHandle start) const
{
assert(is_boundary(start));
boundaryWalkUntil(
@@ -305,13 +350,13 @@ GeoData::boundaryWalk(const std::function<void(HalfedgeHandle)> & op, HalfedgeHa
}
void
-GeoData::boundaryWalkUntil(const std::function<bool(HalfedgeHandle)> & op) const
+GeoData::boundaryWalkUntil(Tester<HalfedgeHandle> op) const
{
boundaryWalkUntil(op, findBoundaryStart());
}
void
-GeoData::boundaryWalkUntil(const std::function<bool(HalfedgeHandle)> & op, HalfedgeHandle start) const
+GeoData::boundaryWalkUntil(Tester<HalfedgeHandle> op, HalfedgeHandle start) const
{
assert(is_boundary(start));
if (!op(start)) {
@@ -468,10 +513,10 @@ GeoData::setHeights(const std::span<const GlobalPosition3D> triangleStrip, const
if (!inter) {
throw std::runtime_error("Perpendicular lines do not cross");
}
- return split(edge_handle(nearest.first), *inter || tsPoint.z);
+ return split_copy(edge_handle(nearest.first), *inter || tsPoint.z);
}
// Nothing close, split face
- return split(face, tsPoint);
+ return split_copy(face, tsPoint);
};
// New vertices for each vertex in triangleStrip
@@ -524,7 +569,7 @@ GeoData::setHeights(const std::span<const GlobalPosition3D> triangleStrip, const
return true;
}
else {
- start = split(edge_handle(next), positionOnTriangle(*intersection, triangle));
+ start = split_copy(edge_handle(next), positionOnTriangle(*intersection, triangle));
}
addVertexForNormalUpdate(start);
boundaryTriangles.emplace(start, &triangle);
@@ -545,8 +590,8 @@ GeoData::setHeights(const std::span<const GlobalPosition3D> triangleStrip, const
triangle++;
};
std::ranges::for_each(newVerts | std::views::adjacent<3>, doBoundary);
- doBoundaryPart(*++newVerts.begin(), newVerts.front(), *strip.rbegin());
- doBoundaryPart(*++newVerts.rbegin(), newVerts.back(), *strip.rbegin());
+ doBoundaryPart(*++newVerts.begin(), newVerts.front(), strip.front());
+ doBoundaryPart(*++newVerts.rbegin(), newVerts.back(), strip.back());
std::set<HalfedgeHandle> done;
std::set<HalfedgeHandle> todo;
@@ -600,7 +645,9 @@ GeoData::setHeights(const std::span<const GlobalPosition3D> triangleStrip, const
});
}
};
- surfaceStripWalk(surfaceStripWalk, findPoint(strip.front().centroid()));
+ for (const auto & triangle : strip) {
+ surfaceStripWalk(surfaceStripWalk, findPoint(triangle.centroid()));
+ }
updateAllVertexNormals(newOrChangedVerts);
generation++;
diff --git a/game/geoData.h b/game/geoData.h
index 92b9b75..e3fc313 100644
--- a/game/geoData.h
+++ b/game/geoData.h
@@ -64,13 +64,25 @@ public:
[[nodiscard]] IntersectionResult intersectRay(const Ray<GlobalPosition3D> &) const;
[[nodiscard]] IntersectionResult intersectRay(const Ray<GlobalPosition3D> &, 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;
+ struct WalkStep {
+ FaceHandle current;
+ FaceHandle previous {};
+ HalfedgeHandle exitHalfedge {};
+ GlobalPosition2D exitPosition {};
+ };
+
+ template<typename T> using Consumer = const std::function<void(const T &)> &;
+ template<typename T> using Tester = const std::function<bool(const T &)> &;
+
+ void walk(const PointFace & from, GlobalPosition2D to, Consumer<WalkStep> op) const;
+ void walkUntil(const PointFace & from, GlobalPosition2D to, Tester<WalkStep> op) const;
+ void walk(const PointFace & from, GlobalPosition2D to, GlobalPosition2D centre, Consumer<WalkStep> op) const;
+ void walkUntil(const PointFace & from, GlobalPosition2D to, GlobalPosition2D centre, Tester<WalkStep> 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;
+ void boundaryWalk(Consumer<HalfedgeHandle>) const;
+ void boundaryWalk(Consumer<HalfedgeHandle>, HalfedgeHandle start) const;
+ void boundaryWalkUntil(Tester<HalfedgeHandle>) const;
+ void boundaryWalkUntil(Tester<HalfedgeHandle>, HalfedgeHandle start) const;
[[nodiscard]] HalfedgeHandle findEntry(const GlobalPosition2D from, const GlobalPosition2D to) const;
@@ -94,9 +106,9 @@ public:
template<typename HandleT>
[[nodiscard]] auto
- get_surface(const HandleT h)
+ getSurface(const HandleT handle) const
{
- return property(surface, h);
+ return property(surface, handle);
}
void sanityCheck() const;
diff --git a/game/network/network.cpp b/game/network/network.cpp
index 6ba3ed6..1666c4d 100644
--- a/game/network/network.cpp
+++ b/game/network/network.cpp
@@ -8,7 +8,13 @@
#include <stdexcept>
#include <utility>
-Network::Network(const std::string & tn) : texture {std::make_shared<Texture>(tn)} { }
+Network::Network(const std::string & tn) :
+ texture {std::make_shared<Texture>(tn,
+ TextureOptions {
+ .minFilter = GL_NEAREST_MIPMAP_LINEAR,
+ })}
+{
+}
Node::Ptr
Network::nodeAt(GlobalPosition3D pos)
diff --git a/game/network/rail.cpp b/game/network/rail.cpp
index dc62cf3..2820cca 100644
--- a/game/network/rail.cpp
+++ b/game/network/rail.cpp
@@ -75,26 +75,31 @@ RailLinks::addLinksBetween(GlobalPosition3D start, GlobalPosition3D end)
return addLink<RailLinkCurve>(start, end, centre.first);
}
-constexpr const std::array<RelativePosition3D, RAIL_CROSSSECTION_VERTICES> railCrossSection {{
- {-1900.F, 0.F, -RAIL_HEIGHT.z * 2},
- {-608.F, 0.F, RAIL_HEIGHT.z},
- {0, 0.F, RAIL_HEIGHT.z / 2},
- {608.F, 0.F, RAIL_HEIGHT.z},
- {1900.F, 0.F, -RAIL_HEIGHT.z * 2},
-}};
-constexpr const std::array<float, RAIL_CROSSSECTION_VERTICES> railTexturePos {
- 0.F,
- .34F,
- .5F,
- .66F,
- 1.F,
-};
-constexpr auto sleepers {5.F}; // There are 5 repetitions of sleepers in the texture
-
-inline auto
-round_sleepers(const float v)
-{
- return round_frac(v, sleepers);
+namespace {
+ constexpr const std::array<RelativePosition3D, RAIL_CROSSSECTION_VERTICES> RAIL_CROSS_SECTION {{
+ {-1330.F, 0.F, 0},
+ {-608.F, 0.F, RAIL_HEIGHT.z},
+ {0, 0.F, RAIL_HEIGHT.z / 2},
+ {608.F, 0.F, RAIL_HEIGHT.z},
+ {1330.F, 0.F, 0},
+ }};
+ constexpr const std::array<float, RAIL_CROSSSECTION_VERTICES> RAIL_TEXTURE_POS {
+ 0.15F,
+ .34F,
+ .5F,
+ .66F,
+ 0.85F,
+ };
+ template<std::floating_point T> constexpr T SLEEPERS_PER_TEXTURE {5};
+ template<std::floating_point T> constexpr T TEXTURE_LENGTH {2'000};
+ template<std::floating_point T> constexpr T SLEEPER_LENGTH {T {1} / SLEEPERS_PER_TEXTURE<T>};
+
+ template<std::floating_point T>
+ constexpr auto
+ roundSleepers(const T length)
+ {
+ return round_frac(length / TEXTURE_LENGTH<T>, SLEEPER_LENGTH<T>);
+ }
}
RailLinkStraight::RailLinkStraight(NetworkLinkHolder<RailLinkStraight> & instances, const Node::Ptr & a,
@@ -106,7 +111,7 @@ RailLinkStraight::RailLinkStraight(
NetworkLinkHolder<RailLinkStraight> & instances, Node::Ptr a, Node::Ptr b, const RelativePosition3D & diff) :
Link({std::move(a), vector_yaw(diff)}, {std::move(b), vector_yaw(-diff)}, glm::length(diff)),
instance {instances.vertices.acquire(
- ends[0].node->pos, ends[1].node->pos, flat_orientation(diff), round_sleepers(length / 2000.F))}
+ ends[0].node->pos, ends[1].node->pos, flat_orientation(diff), roundSleepers(length))}
{
}
@@ -120,9 +125,8 @@ RailLinkCurve::RailLinkCurve(NetworkLinkHolder<RailLinkCurve> & instances, const
GlobalPosition3D c, RelativeDistance radius, const Arc arc) :
Link({a, normalize(arc.first + half_pi)}, {b, normalize(arc.second - half_pi)},
glm::length(RelativePosition2D {radius * arc.length(), difference(a->pos, b->pos).z})),
- LinkCurve {c, radius, arc},
- instance {instances.vertices.acquire(ends[0].node->pos, ends[1].node->pos, c, round_sleepers(length / 2000.F),
- half_pi - arc.first, half_pi - arc.second, radius)}
+ LinkCurve {c, radius, arc}, instance {instances.vertices.acquire(ends[0].node->pos, ends[1].node->pos, c,
+ roundSleepers(length), half_pi - arc.first, half_pi - arc.second, radius)}
{
}
@@ -154,7 +158,7 @@ namespace {
renderType(const NetworkLinkHolder<LinkType> & n, auto & s)
{
if (auto count = n.vertices.size()) {
- s.use(railCrossSection, railTexturePos);
+ s.use(RAIL_CROSS_SECTION, RAIL_TEXTURE_POS);
glBindVertexArray(n.vao);
glDrawArrays(GL_POINTS, 0, static_cast<GLsizei>(count));
}
@@ -166,8 +170,11 @@ RailLinks::render(const SceneShader & shader) const
{
if (!links.objects.empty()) {
texture->bind();
+ glEnable(GL_POLYGON_OFFSET_FILL);
+ glPolygonOffset(-1, 0);
renderType<RailLinkStraight>(*this, shader.networkStraight);
renderType<RailLinkCurve>(*this, shader.networkCurve);
+ glDisable(GL_POLYGON_OFFSET_FILL);
glBindVertexArray(0);
}
}
diff --git a/game/terrain.cpp b/game/terrain.cpp
index e7508d0..39aa99a 100644
--- a/game/terrain.cpp
+++ b/game/terrain.cpp
@@ -43,7 +43,7 @@ Terrain::generateMeshes()
[this, &vertexIndex, &vertices](const GeoData::VertexHandle v) {
std::for_each(geoData->vf_begin(v), geoData->vf_end(v),
[&vertexIndex, v, this, &vertices](const GeoData::FaceHandle f) {
- const auto surface = geoData->get_surface(f);
+ const auto surface = geoData->getSurface(f);
if (const auto vertexIndexRef = vertexIndex.emplace(std::make_pair(v, surface), 0);
vertexIndexRef.second) {
vertexIndexRef.first->second = vertices.size();
@@ -57,7 +57,7 @@ Terrain::generateMeshes()
geoData->faces_sbegin(), 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, f, this](const GeoData::VertexHandle v) {
- return vertexIndex[std::make_pair(v, geoData->get_surface(f))];
+ return vertexIndex[std::make_pair(v, geoData->getSurface(f))];
});
});
meshes.create<MeshT<Vertex>>(vertices, indices);