#include "rail.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) { } std::shared_ptr RailLinks::addLinksBetween(GlobalPosition3D start, GlobalPosition3D end) { auto node1ins = newNodeAt(start), node2ins = newNodeAt(end); if (node1ins.second == NodeIs::NotInNetwork && node2ins.second == NodeIs::NotInNetwork) { // Both nodes are new, direct link, easy return addLink(start, end); } if (node1ins.second == NodeIs::NotInNetwork && node2ins.second == NodeIs::InNetwork) { // node1 is new, node2 exists, but we build from existing outwards std::swap(node1ins, node2ins); std::swap(start, end); } // Find start link/end - opposite entry dir to existing link; so pi +... const Angle dir = pi + findNodeDirection(node1ins.first); if (dir == vector_yaw(difference(end, start))) { return addLink(start, end); } const auto flatStart {start.xy()}, flatEnd {end.xy()}; if (node2ins.second == NodeIs::InNetwork) { auto midheight = [&](auto mid) { const auto sm = glm::length(RelativePosition2D(flatStart - mid)), em = glm::length(RelativePosition2D(flatEnd - mid)); return start.z + GlobalDistance(RelativeDistance(end.z - start.z) * (sm / (sm + em))); }; const float dir2 = pi + findNodeDirection(node2ins.first); if (const auto radii = find_arcs_radius(flatStart, dir, flatEnd, dir2); radii.first < radii.second) { const auto radius {radii.first}; const auto c1 = flatStart + (sincos(dir + half_pi) * radius); const auto c2 = flatEnd + (sincos(dir2 + half_pi) * radius); const auto mid = (c1 + c2) / 2; const auto midh = mid || midheight(mid); addLink(start, midh, c1); return addLink(end, midh, c2); } else { const auto radius {radii.second}; const auto c1 = flatStart + (sincos(dir - half_pi) * radius); const auto c2 = flatEnd + (sincos(dir2 - half_pi) * radius); const auto mid = (c1 + c2) / 2; const auto midh = mid || midheight(mid); addLink(midh, start, c1); return addLink(midh, end, c2); } } const auto diff = difference(end, start); const auto vy {vector_yaw(diff)}; const auto n2ed {(vy * 2) - dir - pi}; const auto centre {find_arc_centre(flatStart, dir, flatEnd, n2ed)}; if (centre.second) { // right hand arc std::swap(start, end); } return addLink(start, end, centre.first); } constexpr const std::array 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 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); } RailLinkStraight::RailLinkStraight(NetworkLinkHolder & instances, const Node::Ptr & a, const Node::Ptr & b) : RailLinkStraight(instances, a, b, b->pos - a->pos) { } RailLinkStraight::RailLinkStraight( NetworkLinkHolder & 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))} { } RailLinkCurve::RailLinkCurve( NetworkLinkHolder & instances, const Node::Ptr & a, const Node::Ptr & b, GlobalPosition2D c) : RailLinkCurve(instances, a, b, c || a->pos.z, glm::length(difference(a->pos.xy(), c)), {c, a->pos, b->pos}) { } RailLinkCurve::RailLinkCurve(NetworkLinkHolder & instances, const Node::Ptr & a, const Node::Ptr & b, 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)} { } 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 & n, auto & s) { if (auto count = n.vertices.size()) { s.use(railCrossSection, railTexturePos); glBindVertexArray(n.vao); glDrawArrays(GL_POINTS, 0, static_cast(count)); } }; } void RailLinks::render(const SceneShader & shader) const { if (!links.objects.empty()) { texture->bind(); renderType(*this, shader.networkStraight); renderType(*this, shader.networkCurve); glBindVertexArray(0); } }