1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
|
#include "rail.h"
#include "game/gamestate.h"
#include "network.h"
#include <game/network/network.impl.h> // IWYU pragma: keep
#include <gfx/gl/sceneShader.h>
#include <gfx/gl/vertexArrayObject.h>
#include <gfx/models/texture.h>
template class NetworkOf<RailLink, RailLinkStraight, RailLinkCurve>;
constexpr auto RAIL_CROSSSECTION_VERTICES {5U};
constexpr Size3D RAIL_HEIGHT {0, 0, 50.F};
RailLinks::RailLinks() : NetworkOf<RailLink, RailLinkStraight, RailLinkCurve> {"rails.jpg"} { }
void
RailLinks::tick(TickDuration)
{
}
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 & nodeA,
const Node::Ptr & nodeB) : RailLinkStraight(instances, nodeA, nodeB, nodeB->pos - nodeA->pos)
{
}
RailLinkStraight::RailLinkStraight(NetworkLinkHolder<RailLinkStraight> & instances, Node::Ptr nodeA, Node::Ptr nodeB,
const RelativePosition3D & diff) :
Link({.node = std::move(nodeA), .dir = vector_yaw(diff)}, {.node = std::move(nodeB), .dir = vector_yaw(-diff)},
glm::length(diff)),
instance {instances.vertices.acquire(
ends[0].node->pos, ends[1].node->pos, flat_orientation(diff), roundSleepers(length))}
{
}
RailLinkCurve::RailLinkCurve(NetworkLinkHolder<RailLinkCurve> & instances, const Node::Ptr & nodeA,
const Node::Ptr & nodeB, GlobalPosition2D centre) :
RailLinkCurve(instances, nodeA, nodeB, centre || nodeA->pos.z, ::distance<2>(nodeA->pos.xy(), centre),
{centre, nodeA->pos, nodeB->pos})
{
}
RailLinkCurve::RailLinkCurve(NetworkLinkHolder<RailLinkCurve> & instances, const Node::Ptr & nodeA,
const Node::Ptr & nodeB, GlobalPosition3D centre, RelativeDistance radius, const Arc arc) :
Link({.node = nodeA, .dir = normalize(arc.first + half_pi)},
{.node = nodeB, .dir = normalize(arc.second - half_pi)},
glm::length(RelativePosition2D {radius * arc.length(), difference(nodeA->pos, nodeB->pos).z})),
LinkCurve {centre, radius, arc}, instance {instances.vertices.acquire(ends[0].node->pos, ends[1].node->pos, centre,
roundSleepers(length), half_pi - arc.first, half_pi - arc.second, radius)}
{
}
RelativePosition3D
RailLink::vehiclePositionOffset() const
{
return RAIL_HEIGHT;
}
template<> NetworkLinkHolder<RailLinkStraight>::NetworkLinkHolder()
{
VertexArrayObject {vao}
.addAttribs<RailLinkStraight::Vertex, &RailLinkStraight::Vertex::a, &RailLinkStraight::Vertex::b,
&RailLinkStraight::Vertex::rotation, &RailLinkStraight::Vertex::textureRepeats>(
vertices.bufferName());
}
template<> NetworkLinkHolder<RailLinkCurve>::NetworkLinkHolder()
{
VertexArrayObject {vao}
.addAttribs<RailLinkCurve::Vertex, &RailLinkCurve::Vertex::a, &RailLinkCurve::Vertex::b,
&RailLinkCurve::Vertex::c, &RailLinkCurve::Vertex::textureRepeats, &RailLinkCurve::Vertex::aangle,
&RailLinkCurve::Vertex::bangle, &RailLinkCurve::Vertex::radius>(vertices.bufferName());
}
namespace {
template<typename LinkType>
void
renderType(const NetworkLinkHolder<LinkType> & networkLinks, auto & shader)
{
if (auto count = networkLinks.vertices.size()) {
shader.use(RAIL_CROSS_SECTION, RAIL_TEXTURE_POS);
glBindVertexArray(networkLinks.vao);
glDrawArrays(GL_POINTS, 0, static_cast<GLsizei>(count));
}
};
}
void
RailLinks::render(const SceneShader & shader, const Frustum &) const
{
if (anyLinks()) {
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);
}
}
const Surface *
RailLinks::getBaseSurface() const
{
return gameState->assets.at("terrain.surface.gravel").dynamicCast<const Surface>().get();
}
RelativeDistance
RailLinks::getBaseWidth() const
{
static constexpr auto BASE_WIDTH = 5'700;
return BASE_WIDTH;
}
|