#include "rail.h" #include "game/gamestate.h" #include "network.h" #include // IWYU pragma: keep #include #include #include template class NetworkOf; constexpr auto RAIL_CROSSSECTION_VERTICES {5U}; constexpr Size3D RAIL_HEIGHT {0, 0, 50.F}; RailLinks::RailLinks() : NetworkOf {"rails.jpg"} { } void RailLinks::tick(TickDuration) { } namespace { constexpr const std::array 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 RAIL_TEXTURE_POS { 0.15F, .34F, .5F, .66F, 0.85F, }; template constexpr T SLEEPERS_PER_TEXTURE {5}; template constexpr T TEXTURE_LENGTH {2'000}; template constexpr T SLEEPER_LENGTH {T {1} / SLEEPERS_PER_TEXTURE}; template constexpr auto roundSleepers(const T length) { return round_frac(length / TEXTURE_LENGTH, SLEEPER_LENGTH); } } RailLinkStraight::RailLinkStraight(NetworkLinkHolder & instances, const Node::Ptr & nodeA, const Node::Ptr & nodeB) : RailLinkStraight(instances, nodeA, nodeB, nodeB->pos - nodeA->pos) { } RailLinkStraight::RailLinkStraight(NetworkLinkHolder & 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 & 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 & 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::NetworkLinkHolder() { VertexArrayObject {vao} .addAttribs( vertices.bufferName()); } template<> NetworkLinkHolder::NetworkLinkHolder() { VertexArrayObject {vao} .addAttribs(vertices.bufferName()); } namespace { template void renderType(const NetworkLinkHolder & 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(count)); } }; } void RailLinks::render(const SceneShader & shader, const Frustum &) const { if (anyLinks()) { texture->bind(); glEnable(GL_POLYGON_OFFSET_FILL); glPolygonOffset(-1, 0); renderType(*this, shader.networkStraight); renderType(*this, shader.networkCurve); glDisable(GL_POLYGON_OFFSET_FILL); glBindVertexArray(0); } } const Surface * RailLinks::getBaseSurface() const { return gameState->assets.at("terrain.surface.gravel").dynamicCast().get(); } RelativeDistance RailLinks::getBaseWidth() const { static constexpr auto BASE_WIDTH = 5'700; return BASE_WIDTH; }