diff options
-rw-r--r-- | .gitmodules | 3 | ||||
-rw-r--r-- | game/network/network.h | 19 | ||||
-rw-r--r-- | game/network/network.impl.h | 47 | ||||
-rw-r--r-- | game/network/rail.cpp | 154 | ||||
-rw-r--r-- | game/network/rail.h | 39 | ||||
-rw-r--r-- | gfx/gl/sceneShader.cpp | 34 | ||||
-rw-r--r-- | gfx/gl/sceneShader.h | 13 | ||||
-rw-r--r-- | gfx/gl/shader.cpp | 37 | ||||
-rw-r--r-- | gfx/gl/shader.h | 11 | ||||
-rw-r--r-- | gfx/gl/shaders/network.fs | 17 | ||||
-rw-r--r-- | gfx/gl/shaders/networkCommon.glsl | 43 | ||||
-rw-r--r-- | gfx/gl/shaders/networkCurve.gs | 47 | ||||
-rw-r--r-- | gfx/gl/shaders/networkCurve.vs | 29 | ||||
-rw-r--r-- | gfx/gl/shaders/networkStraight.gs | 17 | ||||
-rw-r--r-- | gfx/gl/shaders/networkStraight.vs | 24 | ||||
-rw-r--r-- | lib/embed-glsl.cpp.m4 | 9 | ||||
-rw-r--r-- | lib/gl_traits.h | 9 | ||||
-rw-r--r-- | lib/strlen.h | 14 | ||||
-rw-r--r-- | test/test-glContextBhvr.cpp | 3 | ||||
-rw-r--r-- | test/test-network.cpp | 47 | ||||
-rw-r--r-- | test/test-render.cpp | 51 | ||||
m--------- | thirdparty/ctre | 0 |
22 files changed, 494 insertions, 173 deletions
diff --git a/.gitmodules b/.gitmodules index 0ce5431..6332369 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "thirdparty/glad"] path = thirdparty/glad url = https://github.com/Dav1dde/glad +[submodule "thirdparty/ctre"] + path = thirdparty/ctre + url = https://github.com/hanickadot/compile-time-regular-expressions diff --git a/game/network/network.h b/game/network/network.h index fa311ac..5725360 100644 --- a/game/network/network.h +++ b/game/network/network.h @@ -1,5 +1,6 @@ #pragma once +#include "gfx/gl/instanceVertices.h" #include "link.h" #include <collection.h> #include <gfx/renderable.h> @@ -58,7 +59,17 @@ protected: std::shared_ptr<Texture> texture; }; -template<typename T> class NetworkOf : public Network, public Renderable { +template<typename LinkType> class NetworkLinkHolder { +public: + // Implemented per LinkType to configure vao + NetworkLinkHolder(); + friend LinkType; + glVertexArray vao; + mutable InstanceVertices<typename LinkType::Vertex> vertices; +}; + +template<typename T, typename... Links> +class NetworkOf : public Network, public Renderable, public NetworkLinkHolder<Links>... { protected: using Network::Network; @@ -75,7 +86,7 @@ public: requires std::is_base_of_v<T, L> { const auto node1 = candidateNodeAt(a).first, node2 = candidateNodeAt(b).first; - return std::make_shared<L>(node1, node2, std::forward<Params>(params)...); + return std::make_shared<L>(*this, node1, node2, std::forward<Params>(params)...); } template<typename L, typename... Params> @@ -84,7 +95,7 @@ public: requires std::is_base_of_v<T, L> { const auto node1 = nodeAt(a), node2 = nodeAt(b); - auto l {links.template create<L>(node1, node2, std::forward<Params>(params)...)}; + auto l {links.template create<L>(*this, node1, node2, std::forward<Params>(params)...)}; joinLinks(l); return l; } @@ -98,8 +109,6 @@ public: [[nodiscard]] float findNodeDirection(Node::AnyCPtr) const override; - void render(const SceneShader &) const override; - protected: Link::CCollection addJoins(); }; diff --git a/game/network/network.impl.h b/game/network/network.impl.h index f9595ed..ff29088 100644 --- a/game/network/network.impl.h +++ b/game/network/network.impl.h @@ -2,29 +2,18 @@ #include <gfx/gl/sceneShader.h> #include <gfx/models/texture.h> -template<typename T> +template<typename T, typename... Links> void -NetworkOf<T>::render(const SceneShader & shader) const -{ - if constexpr (std::is_base_of_v<Renderable, T>) { - shader.absolute.use(); - texture->bind(); - links.apply(&Renderable::render, shader); - } -} - -template<typename T> -void -NetworkOf<T>::joinLinks(const Link::Ptr & l) const +NetworkOf<T, Links...>::joinLinks(const Link::Ptr & l) const { for (const auto & ol : links.objects) { Network::joinLinks(l, ol); } } -template<typename T> +template<typename T, typename... Links> Link::Ptr -NetworkOf<T>::intersectRayLinks(const Ray<GlobalPosition3D> & ray) const +NetworkOf<T, Links...>::intersectRayLinks(const Ray<GlobalPosition3D> & ray) const { // Click link if (const auto link = std::find_if(links.objects.begin(), links.objects.end(), @@ -37,9 +26,9 @@ NetworkOf<T>::intersectRayLinks(const Ray<GlobalPosition3D> & ray) const return {}; } -template<typename T> +template<typename T, typename... Links> float -NetworkOf<T>::findNodeDirection(Node::AnyCPtr n) const +NetworkOf<T, Links...>::findNodeDirection(Node::AnyCPtr n) const { for (const auto & l : links.objects) { for (const auto & e : l->ends) { @@ -52,16 +41,16 @@ NetworkOf<T>::findNodeDirection(Node::AnyCPtr n) const throw std::runtime_error("Node exists but couldn't find it"); } -template<typename T> +template<typename T, typename... Links> Link::CCollection -NetworkOf<T>::candidateStraight(GlobalPosition3D n1, GlobalPosition3D n2) +NetworkOf<T, Links...>::candidateStraight(GlobalPosition3D n1, GlobalPosition3D n2) { return {candidateLink<typename T::StraightLink>(n1, n2)}; } -template<typename T> +template<typename T, typename... Links> Link::CCollection -NetworkOf<T>::candidateJoins(GlobalPosition3D start, GlobalPosition3D end) +NetworkOf<T, Links...>::candidateJoins(GlobalPosition3D start, GlobalPosition3D end) { if (glm::length(RelativePosition3D(start - end)) < 2000.F) { return {}; @@ -73,24 +62,24 @@ NetworkOf<T>::candidateJoins(GlobalPosition3D start, GlobalPosition3D end) return {candidateLink<typename T::CurveLink>(c1s, c1e, c1c), candidateLink<typename T::CurveLink>(c2s, c2e, c2c)}; } -template<typename T> +template<typename T, typename... Links> Link::CCollection -NetworkOf<T>::candidateExtend(GlobalPosition3D start, GlobalPosition3D end) +NetworkOf<T, Links...>::candidateExtend(GlobalPosition3D start, GlobalPosition3D end) { const auto [cstart, cend, centre] = genCurveDef(start, end, findNodeDirection(candidateNodeAt(start).first)); return {candidateLink<typename T::CurveLink>(cstart, cend, centre)}; } -template<typename T> +template<typename T, typename... Links> Link::CCollection -NetworkOf<T>::addStraight(GlobalPosition3D n1, GlobalPosition3D n2) +NetworkOf<T, Links...>::addStraight(GlobalPosition3D n1, GlobalPosition3D n2) { return {addLink<typename T::StraightLink>(n1, n2)}; } -template<typename T> +template<typename T, typename... Links> Link::CCollection -NetworkOf<T>::addJoins(GlobalPosition3D start, GlobalPosition3D end) +NetworkOf<T, Links...>::addJoins(GlobalPosition3D start, GlobalPosition3D end) { if (glm::length(RelativePosition3D(start - end)) < 2000.F) { return {}; @@ -101,9 +90,9 @@ NetworkOf<T>::addJoins(GlobalPosition3D start, GlobalPosition3D end) return {addLink<typename T::CurveLink>(c1s, c1e, c1c), addLink<typename T::CurveLink>(c2s, c2e, c2c)}; } -template<typename T> +template<typename T, typename... Links> Link::CCollection -NetworkOf<T>::addExtend(GlobalPosition3D start, GlobalPosition3D end) +NetworkOf<T, Links...>::addExtend(GlobalPosition3D start, GlobalPosition3D 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 79aaf97..176a704 100644 --- a/game/network/rail.cpp +++ b/game/network/rail.cpp @@ -1,24 +1,16 @@ #include "rail.h" #include "network.h" -#include <array> -#include <cmath> -#include <collection.h> -#include <cstddef> -#include <game/network/link.h> #include <game/network/network.impl.h> // IWYU pragma: keep -#include <gfx/models/vertex.h> -#include <glad/gl.h> -#include <glm/gtx/transform.hpp> -#include <maths.h> -#include <utility> -#include <vector> +#include <gfx/gl/sceneShader.h> +#include <gfx/gl/vertexArrayObject.h> +#include <gfx/models/texture.h> -template class NetworkOf<RailLink>; +template class NetworkOf<RailLink, RailLinkStraight, RailLinkCurve>; constexpr auto RAIL_CROSSSECTION_VERTICES {5U}; constexpr Size3D RAIL_HEIGHT {0, 0, 250.F}; -RailLinks::RailLinks() : NetworkOf<RailLink> {"rails.jpg"} { } +RailLinks::RailLinks() : NetworkOf<RailLink, RailLinkStraight, RailLinkCurve> {"rails.jpg"} { } void RailLinks::tick(TickDuration) @@ -81,34 +73,20 @@ RailLinks::addLinksBetween(GlobalPosition3D start, GlobalPosition3D end) return addLink<RailLinkCurve>(start, end, centre.first); } -Mesh::Ptr -RailLink::defaultMesh(const std::span<Vertex> vertices) -{ - std::vector<unsigned int> indices; - for (auto n = RAIL_CROSSSECTION_VERTICES; n < vertices.size(); n += 1) { - indices.push_back(n - RAIL_CROSSSECTION_VERTICES); - indices.push_back(n); - } - - return std::make_unique<Mesh>(vertices, indices, GL_TRIANGLE_STRIP); -} - -void -RailLink::render(const SceneShader &) const -{ - mesh->Draw(); -} - -constexpr const std::array<std::pair<RelativePosition3D, float>, RAIL_CROSSSECTION_VERTICES> railCrossSection {{ - // ___________ - // _/ \_ - // left to right - {{-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 const std::array<RelativePosition3D, RAIL_CROSSSECTION_VERTICES> railCrossSection {{ + {-1900.F, 0.F, 0.F}, + {-608.F, 0.F, RAIL_HEIGHT.z}, + {0, 0.F, RAIL_HEIGHT.z * .7F}, + {608.F, 0.F, RAIL_HEIGHT.z}, + {1900.F, 0.F, 0.F}, }}; +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 @@ -117,58 +95,34 @@ round_sleepers(const float v) return round_frac(v, sleepers); } -RailLinkStraight::RailLinkStraight(const Node::Ptr & a, const Node::Ptr & b) : RailLinkStraight(a, b, b->pos - a->pos) +RailLinkStraight::RailLinkStraight( + NetworkLinkHolder<RailLinkStraight> & instances, const Node::Ptr & a, const Node::Ptr & b) : + RailLinkStraight(instances, a, b, b->pos - a->pos) { } -RailLinkStraight::RailLinkStraight(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)) +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))} { - if (glGenVertexArrays) { - std::vector<Vertex> vertices; - vertices.reserve(2 * railCrossSection.size()); - const auto len = round_sleepers(length / 2000.F); - const glm::mat3 trans {flat_orientation(diff)}; - for (auto ei : {1U, 0U}) { - for (const auto & rcs : railCrossSection) { - const auto m {ends[ei].node->pos + GlobalPosition3D(trans * rcs.first)}; - vertices.emplace_back(m, TextureRelCoord {rcs.second, len * static_cast<float>(ei)}, up); - } - } - mesh = defaultMesh(vertices); - } } -RailLinkCurve::RailLinkCurve(const Node::Ptr & a, const Node::Ptr & b, GlobalPosition2D c) : - RailLinkCurve(a, b, c || a->pos.z, {c || 0, a->pos, b->pos}) +RailLinkCurve::RailLinkCurve( + NetworkLinkHolder<RailLinkCurve> & instances, const Node::Ptr & a, const Node::Ptr & b, GlobalPosition2D c) : + RailLinkCurve(instances, a, b, c || a->pos.z, {c || 0, a->pos, b->pos}) { } -RailLinkCurve::RailLinkCurve(const Node::Ptr & a, const Node::Ptr & b, GlobalPosition3D c, const Arc arc) : +RailLinkCurve::RailLinkCurve(NetworkLinkHolder<RailLinkCurve> & instances, const Node::Ptr & a, const Node::Ptr & b, + GlobalPosition3D c, const Arc arc) : Link({a, normalize(arc.first + half_pi)}, {b, normalize(arc.second - half_pi)}, glm::length(RelativePosition3D(a->pos - c)) * arc_length(arc)), - LinkCurve {c, glm::length(RelativePosition3D(ends[0].node->pos - c)), arc} + LinkCurve {c, glm::length(RelativePosition3D(ends[0].node->pos - c)), 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)} { - if (glGenVertexArrays) { - 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(slength / std::pow(radius, 0.7F)); - const auto step {RelativePosition3D {arc_length(arc), e1p.z - e0p.z, slength / 1000.F} / segs}; - - auto segCount = static_cast<std::size_t>(std::lround(segs)) + 1; - std::vector<Vertex> vertices; - vertices.reserve(segCount * railCrossSection.size()); - for (RelativePosition3D swing = {arc.first, centreBase.z - e0p.z, 0.F}; segCount; swing += step, --segCount) { - const auto t { - glm::rotate(half_pi - swing.x, up) * glm::translate(RelativePosition3D {radius, 0.F, swing.y})}; - for (const auto & rcs : railCrossSection) { - const auto m {centreBase + GlobalPosition3D(t * (rcs.first || 1.F))}; - vertices.emplace_back(m, TextureRelCoord {rcs.second, swing.z}, up); - } - } - mesh = defaultMesh(vertices); - } } RelativePosition3D @@ -176,3 +130,43 @@ 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> & n, auto & s) + { + if (auto count = n.vertices.size()) { + s.use(railCrossSection, railTexturePos); + glBindVertexArray(n.vao); + glDrawArrays(GL_POINTS, 0, static_cast<GLsizei>(count)); + } + }; +} + +void +RailLinks::render(const SceneShader & shader) const +{ + if (!links.objects.empty()) { + texture->bind(); + renderType<RailLinkStraight>(*this, shader.networkStraight); + renderType<RailLinkCurve>(*this, shader.networkCurve); + glBindVertexArray(0); + } +} diff --git a/game/network/rail.h b/game/network/rail.h index 986b0aa..c8effef 100644 --- a/game/network/rail.h +++ b/game/network/rail.h @@ -2,13 +2,11 @@ #include "chronology.h" #include "game/worldobject.h" -#include "gfx/models/mesh.h" #include "gfx/renderable.h" #include "link.h" #include "network.h" #include <glm/glm.hpp> #include <memory> -#include <span> #include <special_members.h> class SceneShader; @@ -19,7 +17,7 @@ struct Arc; class RailLinkStraight; class RailLinkCurve; -class RailLink : public virtual Link, public Renderable { +class RailLink : public virtual Link { public: RailLink() = default; inline ~RailLink() override = 0; @@ -27,40 +25,57 @@ public: using StraightLink = RailLinkStraight; using CurveLink = RailLinkCurve; - void render(const SceneShader &) const override; NO_COPY(RailLink); NO_MOVE(RailLink); protected: [[nodiscard]] RelativePosition3D vehiclePositionOffset() const override; - [[nodiscard]] static Mesh::Ptr defaultMesh(const std::span<Vertex> vertices); - - Mesh::Ptr mesh; }; RailLink::~RailLink() = default; +class RailLinks; + class RailLinkStraight : public RailLink, public LinkStraight { public: - RailLinkStraight(const Node::Ptr &, const Node::Ptr &); + RailLinkStraight(NetworkLinkHolder<RailLinkStraight> &, const Node::Ptr &, const Node::Ptr &); + + struct Vertex { + GlobalPosition3D a, b; + glm::mat2 rotation; + float textureRepeats; + }; private: - RailLinkStraight(Node::Ptr, Node::Ptr, const RelativePosition3D & diff); + RailLinkStraight(NetworkLinkHolder<RailLinkStraight> &, Node::Ptr, Node::Ptr, const RelativePosition3D & diff); + InstanceVertices<Vertex>::InstanceProxy instance; }; class RailLinkCurve : public RailLink, public LinkCurve { public: - RailLinkCurve(const Node::Ptr &, const Node::Ptr &, GlobalPosition2D); + RailLinkCurve(NetworkLinkHolder<RailLinkCurve> &, const Node::Ptr &, const Node::Ptr &, GlobalPosition2D); + + struct Vertex { + GlobalPosition3D a, b, c; + float textureRepeats; + float aangle, bangle, radius; + }; private: - RailLinkCurve(const Node::Ptr &, const Node::Ptr &, GlobalPosition3D, const Arc); + RailLinkCurve( + NetworkLinkHolder<RailLinkCurve> &, const Node::Ptr &, const Node::Ptr &, GlobalPosition3D, const Arc); + InstanceVertices<Vertex>::InstanceProxy instance; }; -class RailLinks : public NetworkOf<RailLink>, public WorldObject { +template<> NetworkLinkHolder<RailLinkStraight>::NetworkLinkHolder(); +template<> NetworkLinkHolder<RailLinkCurve>::NetworkLinkHolder(); + +class RailLinks : public NetworkOf<RailLink, RailLinkStraight, RailLinkCurve>, public WorldObject { public: RailLinks(); std::shared_ptr<RailLink> addLinksBetween(GlobalPosition3D start, GlobalPosition3D end); + void render(const SceneShader &) const override; private: void tick(TickDuration elapsed) override; diff --git a/gfx/gl/sceneShader.cpp b/gfx/gl/sceneShader.cpp index 1b3b27c..bc64a88 100644 --- a/gfx/gl/sceneShader.cpp +++ b/gfx/gl/sceneShader.cpp @@ -1,14 +1,19 @@ #include "sceneShader.h" #include <gfx/gl/shaders/fs-landmass.h> #include <gfx/gl/shaders/fs-material.h> +#include <gfx/gl/shaders/fs-network.h> #include <gfx/gl/shaders/fs-pointLight.h> #include <gfx/gl/shaders/fs-spotLight.h> #include <gfx/gl/shaders/fs-water.h> +#include <gfx/gl/shaders/gs-networkCurve.h> +#include <gfx/gl/shaders/gs-networkStraight.h> #include <gfx/gl/shaders/gs-pointLight.h> #include <gfx/gl/shaders/gs-spotLight.h> #include <gfx/gl/shaders/vs-dynamicPoint.h> #include <gfx/gl/shaders/vs-dynamicPointInst.h> #include <gfx/gl/shaders/vs-fixedPoint.h> +#include <gfx/gl/shaders/vs-networkCurve.h> +#include <gfx/gl/shaders/vs-networkStraight.h> #include <gfx/gl/shaders/vs-pointLight.h> #include <gfx/gl/shaders/vs-spotLight.h> #include <gfx/gl/shaders/vs-water.h> @@ -21,15 +26,17 @@ SceneShader::SceneShader() : basicInst {dynamicPointInst_vs, material_fs}, landmass {fixedPoint_vs, landmass_fs}, absolute {fixedPoint_vs, material_fs}, spotLightInst {spotLight_vs, spotLight_gs, spotLight_fs}, - pointLightInst {pointLight_vs, pointLight_gs, pointLight_fs} + pointLightInst {pointLight_vs, pointLight_gs, pointLight_fs}, + networkStraight {networkStraight_vs, networkStraight_gs, network_fs}, + networkCurve {networkCurve_vs, networkCurve_gs, network_fs} { } void SceneShader::setViewProjection(const GlobalPosition3D & viewPoint, const glm::mat4 & viewProjection) const { - for (const auto & prog : std::initializer_list<const SceneProgram *> { - &basic, &basicInst, &water, &landmass, &absolute, &pointLightInst, &spotLightInst}) { + for (const auto & prog : std::initializer_list<const SceneProgram *> {&basic, &basicInst, &water, &landmass, + &absolute, &pointLightInst, &spotLightInst, &networkStraight, &networkCurve}) { prog->setViewProjection(viewPoint, viewProjection); } } @@ -37,8 +44,8 @@ SceneShader::setViewProjection(const GlobalPosition3D & viewPoint, const glm::ma void SceneShader::setViewPort(const ViewPort & viewPort) const { - for (const auto & prog : std::initializer_list<const SceneProgram *> { - &basic, &basicInst, &water, &landmass, &absolute, &pointLightInst, &spotLightInst}) { + for (const auto & prog : std::initializer_list<const SceneProgram *> {&basic, &basicInst, &water, &landmass, + &absolute, &pointLightInst, &spotLightInst, &networkStraight, &networkCurve}) { prog->setViewPort(viewPort); } } @@ -79,6 +86,23 @@ SceneShader::BasicProgram::use(Location const & location) const setModel(location); } +template<typename... S> +SceneShader::NetworkProgram::NetworkProgram(S &&... s) : + AbsolutePosProgram {std::forward<S>(s)...}, profileLoc {*this, "profile"}, texturePosLoc {*this, "texturePos"}, + profileLengthLoc {*this, "profileLength"} +{ +} + +void +SceneShader::NetworkProgram::use( + const std::span<const glm::vec3> profile, const std::span<const float> texturePos) const +{ + Program::use(); + glUniform(profileLoc, profile); + glUniform(texturePosLoc, texturePos); + glUniform(profileLengthLoc, static_cast<GLuint>(profile.size())); +} + SceneShader::WaterProgram::WaterProgram() : SceneProgram {water_vs, water_fs}, waveLoc {*this, "waves"} { } void diff --git a/gfx/gl/sceneShader.h b/gfx/gl/sceneShader.h index 813c1bf..8621442 100644 --- a/gfx/gl/sceneShader.h +++ b/gfx/gl/sceneShader.h @@ -3,6 +3,7 @@ #include "config/types.h" #include "program.h" #include <glArrays.h> +#include <span> class Location; @@ -41,8 +42,17 @@ class SceneShader { using SceneProgram::SceneProgram; }; - class WaterProgram : public SceneProgram { + class NetworkProgram : public AbsolutePosProgram { public: + template<typename... S> explicit NetworkProgram(S &&...); + + void use(const std::span<const glm::vec3>, const std::span<const float>) const; + + private: + RequiredUniformLocation profileLoc, texturePosLoc, profileLengthLoc; + }; + + class WaterProgram : public SceneProgram { public: WaterProgram(); void use(float waveCycle) const; @@ -57,6 +67,7 @@ public: BasicProgram basic; WaterProgram water; AbsolutePosProgram basicInst, landmass, absolute, spotLightInst, pointLightInst; + NetworkProgram networkStraight, networkCurve; void setViewProjection(const GlobalPosition3D & viewPoint, const glm::mat4 & viewProjection) const; void setViewPort(const ViewPort & viewPort) const; diff --git a/gfx/gl/shader.cpp b/gfx/gl/shader.cpp index 5f83b83..0f75817 100644 --- a/gfx/gl/shader.cpp +++ b/gfx/gl/shader.cpp @@ -1,13 +1,48 @@ #include "shader.h" +#include <algorithm> #include <array> #include <stdexcept> #include <string> +namespace { + auto + getInt(GLenum e) + { + GLint i {}; + glGetIntegerv(e, &i); + return std::to_string(i); + } + + using LookUpFunction = std::string (*)(GLenum); + constexpr std::array<std::tuple<std::string_view, GLenum, LookUpFunction>, 1> LOOKUPS {{ + {"GL_MAX_GEOMETRY_OUTPUT_VERTICES", GL_MAX_GEOMETRY_OUTPUT_VERTICES, getInt}, + }}; +} + Shader::ShaderRef Shader::compile() const { ShaderRef shader {type}; - glShaderSource(shader, 1, &text, &len); + auto source = [&shader](auto text, GLint len) { + glShaderSource(shader, 1, &text, &len); + }; + if (lookups) { + std::basic_string<GLchar> textMod {text}; + for (const auto & match : ctre::range<R"(\bGL_[A-Z_]+\b)">(textMod)) { + if (const auto lookup = std::find_if(LOOKUPS.begin(), LOOKUPS.end(), + [&match](const auto & lookup) { + return std::get<std::string_view>(lookup) == match; + }); + lookup != LOOKUPS.end()) { + const auto & [name, pname, getFunction] = *lookup; + textMod.replace(match.begin(), match.end(), getFunction(pname)); + } + } + source(textMod.c_str(), static_cast<GLint>(textMod.length())); + } + else { + source(text.data(), static_cast<GLint>(text.length())); + } glCompileShader(shader); CheckShaderError(shader, GL_COMPILE_STATUS, false, "Error compiling shader!"); diff --git a/gfx/gl/shader.h b/gfx/gl/shader.h index cff2281..c6b45af 100644 --- a/gfx/gl/shader.h +++ b/gfx/gl/shader.h @@ -2,20 +2,23 @@ #include <glRef.h> #include <glad/gl.h> -#include <optional> #include <string_view> +#include <thirdparty/ctre/include/ctre.hpp> class Shader { public: using ShaderRef = glRef<GLuint, &glCreateShader, &glDeleteShader>; - constexpr Shader(const GLchar * text, GLint len, GLuint type) : text {text}, len {len}, type {type} { } + constexpr Shader(const GLchar * text, GLuint type) : + text {text}, type {type}, lookups {ctre::search<R"(\bGL_[A-Z_]+\b)">(this->text)} + { + } [[nodiscard]] ShaderRef compile() const; static void CheckShaderError(GLuint shader, GLuint flag, bool isProgram, std::string_view errorMessage); private: - const GLchar * text; - GLint len; + const std::basic_string_view<GLchar> text; GLuint type; + bool lookups; }; diff --git a/gfx/gl/shaders/network.fs b/gfx/gl/shaders/network.fs new file mode 100644 index 0000000..4e347b4 --- /dev/null +++ b/gfx/gl/shaders/network.fs @@ -0,0 +1,17 @@ +#version 330 core +#extension GL_ARB_shading_language_420pack : enable + +include(`materialOut.glsl') +in vec3 rposition; +in vec2 texCoord; + +layout(binding = 0) uniform sampler2D texture0; +uniform ivec3 viewPoint; + +void +main() +{ + gPosition = ivec4(viewPoint + rposition, 1); + gNormal = vec4(0, 0, 1, 1); + gAlbedoSpec = texture(texture0, texCoord); +} diff --git a/gfx/gl/shaders/networkCommon.glsl b/gfx/gl/shaders/networkCommon.glsl new file mode 100644 index 0000000..0bc3c1c --- /dev/null +++ b/gfx/gl/shaders/networkCommon.glsl @@ -0,0 +1,43 @@ +uniform vec3[10] profile; +uniform float[10] texturePos; +uniform uint profileLength; + +uniform mat4 viewProjection; +uniform ivec3 viewPoint; + +uniform float clipDistance = 5000000; +uniform float flatDistance = 1000000; + +out vec2 texCoord; +out vec3 rposition; + +float +segDist(const ivec3 a, const ivec3 b) +{ + return min(distance(viewPoint, a), distance(viewPoint, b)); +} + +ifelse( + TYPE, .gs, + // Begin: Geometry shader only function + void doVertex(const ivec3 end, const uint v, const float texY, const mat2 rot) { + ivec3 vpos = end + ivec3(rot * profile[v].xy, profile[v].z); + rposition = vpos - viewPoint; + gl_Position = viewProjection * vec4(rposition, 1); + texCoord = vec2(texturePos[v], texY); + EmitVertex(); + } + + void doSeg(const float dist, const ivec3 apos, const ivec3 bpos, const float atexY, const float btexY, + const mat2 arot, const mat2 brot) { + if (dist < clipDistance) { + uint vstep = (dist < flatDistance) ? 1u : profileLength - 1u; + for (uint v = 0u; v < profileLength; v += vstep) { + doVertex(bpos, v, btexY, brot); + doVertex(apos, v, atexY, arot); + } + EndPrimitive(); + } + } + // End: Geometry shader only function +) diff --git a/gfx/gl/shaders/networkCurve.gs b/gfx/gl/shaders/networkCurve.gs new file mode 100644 index 0000000..7cb6c42 --- /dev/null +++ b/gfx/gl/shaders/networkCurve.gs @@ -0,0 +1,47 @@ +#version 330 core + +flat in ivec3 apos[]; +flat in ivec3 bpos[]; +flat in ivec3 cpos[]; +flat in float reps[]; +flat in float aangle[]; +flat in float bangle[]; +flat in float radius[]; + +layout(points) in; +layout(triangle_strip, max_vertices = GL_MAX_GEOMETRY_OUTPUT_VERTICES) out; + +const mat2 rot = mat2(1); + +include(`networkCommon.glsl') + +mat2 +getRot(float angle) +{ + return mat2(cos(angle), sin(angle), -sin(angle), cos(angle)); +} + +void +main() +{ + float segs = clamp( + round(reps[0] * radius[0] / 1000), 4, floor(uint(GL_MAX_GEOMETRY_OUTPUT_VERTICES) / (profileLength * 2u))); + vec3 arcstep = vec3((bangle[0] - aangle[0]), // angle + reps[0], // texture + (bpos[0].z - apos[0].z)) // height + / segs; + + ivec3 prevPos = apos[0]; + mat2 prevRot = getRot(aangle[0]); + float prevTex = 0; + for (vec3 arc = arcstep; arc.y < reps[0] - 0.01; arc += arcstep) { + mat2 rot = getRot(arc.x + aangle[0]); + ivec3 pos = cpos[0] + ivec3(rot * vec2(radius[0], 0), arc.z); + float tex = arc.y; + doSeg(segDist(prevPos, pos), pos, prevPos, tex, prevTex, rot, prevRot); + prevPos = pos; + prevRot = rot; + prevTex = tex; + } + doSeg(segDist(prevPos, bpos[0]), bpos[0], prevPos, reps[0], prevTex, getRot(bangle[0]), prevRot); +} diff --git a/gfx/gl/shaders/networkCurve.vs b/gfx/gl/shaders/networkCurve.vs new file mode 100644 index 0000000..f51bb87 --- /dev/null +++ b/gfx/gl/shaders/networkCurve.vs @@ -0,0 +1,29 @@ +#version 330 core + +layout(location = 0) in ivec3 v_apos; +layout(location = 1) in ivec3 v_bpos; +layout(location = 2) in ivec3 v_centre; +layout(location = 3) in float v_reps; +layout(location = 4) in float v_aangle; +layout(location = 5) in float v_bangle; +layout(location = 6) in float v_radius; + +flat out ivec3 apos; +flat out ivec3 bpos; +flat out ivec3 cpos; +flat out float reps; +flat out float aangle; +flat out float bangle; +flat out float radius; + +void +main() +{ + apos = v_apos; + bpos = v_bpos; + cpos = v_centre; + reps = v_reps; + aangle = v_aangle; + bangle = v_bangle; + radius = v_radius; +} diff --git a/gfx/gl/shaders/networkStraight.gs b/gfx/gl/shaders/networkStraight.gs new file mode 100644 index 0000000..51df5fb --- /dev/null +++ b/gfx/gl/shaders/networkStraight.gs @@ -0,0 +1,17 @@ +#version 330 core + +flat in ivec3 apos[]; +flat in ivec3 bpos[]; +flat in mat2 rot[]; +flat in float reps[]; +flat in float dist[]; + +layout(points) in; +layout(triangle_strip, max_vertices = 10) out; +include(`networkCommon.glsl') + +void +main() +{ + doSeg(dist[0], apos[0], bpos[0], 0.f, reps[0], rot[0], rot[0]); +} diff --git a/gfx/gl/shaders/networkStraight.vs b/gfx/gl/shaders/networkStraight.vs new file mode 100644 index 0000000..55f9c4f --- /dev/null +++ b/gfx/gl/shaders/networkStraight.vs @@ -0,0 +1,24 @@ +#version 330 core + +layout(location = 0) in ivec3 v_apos; +layout(location = 1) in ivec3 v_bpos; +layout(location = 2) in mat2 v_rot; +layout(location = 4) in float v_reps; + +flat out ivec3 apos; +flat out ivec3 bpos; +flat out mat2 rot; +flat out float reps; +flat out float dist; + +include(`networkCommon.glsl') + +void +main() +{ + apos = v_apos; + bpos = v_bpos; + rot = v_rot; + reps = v_reps; + dist = segDist(v_apos, v_bpos); +} diff --git a/lib/embed-glsl.cpp.m4 b/lib/embed-glsl.cpp.m4 index 60fb56b..9fe0b41 100644 --- a/lib/embed-glsl.cpp.m4 +++ b/lib/embed-glsl.cpp.m4 @@ -1,12 +1,9 @@ changecom() dnl // NAME #include "gfx/gl/shader.h" -#include "lib/strlen.h" #include "substr(TYPE,1)-NAME.h" #include <glad/gl.h> - constexpr const GLchar * src {R"GLSL-EMBED(dnl -include(SOURCE))GLSL-EMBED"}; -constexpr auto len {constexpr_strlen(src)}; - -const Shader NAME`_'substr(TYPE,1) { src, len, GLTYPE }; +constexpr Shader NAME`_'substr(TYPE,1) { + R"GLSL-EMBED(dnl + include(SOURCE))GLSL-EMBED", GLTYPE }; diff --git a/lib/gl_traits.h b/lib/gl_traits.h index 2d3d85d..dcbe04e 100644 --- a/lib/gl_traits.h +++ b/lib/gl_traits.h @@ -125,6 +125,15 @@ glUniform(GLint location, std::span<const glm::vec<L, T, Q>> v) (*gl_traits<T>::glUniformvFunc[L - 1])(location, static_cast<GLsizei>(v.size()), glm::value_ptr(v.front())); } +template<typename T> +void +glUniform(GLint location, std::span<const T> v) +{ + static_assert( + requires { gl_traits<T>::glUniformvFunc; }, "Has glUnformNTv"); + (*gl_traits<T>::glUniformvFunc.front())(location, static_cast<GLsizei>(v.size()), v.data()); +} + template<glm::length_t L, typename T, glm::qualifier Q> void glUniform(GLint location, const glm::mat<L, L, T, Q> & v) diff --git a/lib/strlen.h b/lib/strlen.h deleted file mode 100644 index 2090d25..0000000 --- a/lib/strlen.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include <cstddef> -#include <glad/gl.h> - -constexpr auto -constexpr_strlen(const GLchar * const s) -{ - std::size_t ch {}; - while (s[ch]) { - ch++; - } - return ch; -} diff --git a/test/test-glContextBhvr.cpp b/test/test-glContextBhvr.cpp index 26b45d9..ec5cc21 100644 --- a/test/test-glContextBhvr.cpp +++ b/test/test-glContextBhvr.cpp @@ -1,10 +1,9 @@ #define BOOST_TEST_MODULE test_glcontextbehaviour - -#include "testHelpers.h" #include <boost/test/unit_test.hpp> #include <gfx/gl/sceneRenderer.h> #include <lib/glArrays.h> +#include <optional> #include <ui/applicationBase.h> #include <ui/window.h> diff --git a/test/test-network.cpp b/test/test-network.cpp index 0274b00..174e2a5 100644 --- a/test/test-network.cpp +++ b/test/test-network.cpp @@ -23,25 +23,39 @@ BOOST_GLOBAL_FIXTURE(ApplicationBase); BOOST_GLOBAL_FIXTURE(TestMainWindow); -struct TestLink : public LinkStraight { - TestLink(const Node::Ptr & a, const Node::Ptr & b) : TestLink {a, b, (a->pos - b->pos)} { } +struct TestLinkS; - TestLink(Node::Ptr a, Node::Ptr b, RelativePosition2D l) : +struct TestLink : public virtual Link { + using StraightLink = TestLinkS; + using CurveLink = TestLinkS; +}; + +struct TestLinkS : public TestLink, public LinkStraight { + TestLinkS(NetworkLinkHolder<TestLinkS> & network, const Node::Ptr & a, const Node::Ptr & b) : + TestLinkS {network, a, b, (a->pos - b->pos)} + { + } + + TestLinkS(NetworkLinkHolder<TestLinkS> &, Node::Ptr a, Node::Ptr b, RelativePosition2D l) : Link {{std::move(a), 0}, {std::move(b), pi}, glm::length(l)} { } - TestLink(Node::Ptr a, Node::Ptr b, float l) : Link {{std::move(a), 0}, {std::move(b), pi}, l} { } + struct Vertex { }; - using StraightLink = TestLink; - using CurveLink = TestLink; + TestLinkS(NetworkLinkHolder<TestLinkS> &, Node::Ptr a, Node::Ptr b, float l) : + Link {{std::move(a), 0}, {std::move(b), pi}, l} + { + } }; constexpr GlobalPosition3D p000 {0, 0, 0}, p100 {10000, 0, 0}, p200 {20000, 0, 0}, p300 {30000, 0, 0}; constexpr GlobalPosition3D p110 {10000, 10000, 0}; -struct TestNetwork : public NetworkOf<TestLink> { - TestNetwork() : NetworkOf<TestLink> {RESDIR "rails.jpg"} +template<> NetworkLinkHolder<TestLinkS>::NetworkLinkHolder() = default; + +struct TestNetwork : public NetworkOf<TestLink, TestLinkS> { + TestNetwork() : NetworkOf<TestLink, TestLinkS> {RESDIR "rails.jpg"} { // 0 1 2 // p000 <-> p100 <-> p200 <-> p300 @@ -49,12 +63,17 @@ struct TestNetwork : public NetworkOf<TestLink> { // \ 5 / // 3 | 4 // \-> p110 <-/ - addLink<TestLink>(p000, p100, 1.F); - addLink<TestLink>(p100, p200, 1.F); - addLink<TestLink>(p200, p300, 1.F); - addLink<TestLink>(p000, p110, 2.F); - addLink<TestLink>(p200, p110, 2.F); - addLink<TestLink>(p100, p110, 1.F); + addLink<TestLinkS>(p000, p100, 1.F); + addLink<TestLinkS>(p100, p200, 1.F); + addLink<TestLinkS>(p200, p300, 1.F); + addLink<TestLinkS>(p000, p110, 2.F); + addLink<TestLinkS>(p200, p110, 2.F); + addLink<TestLinkS>(p100, p110, 1.F); + } + + void + render(const SceneShader &) const override + { } }; diff --git a/test/test-render.cpp b/test/test-render.cpp index 41731dd..6c20a23 100644 --- a/test/test-render.cpp +++ b/test/test-render.cpp @@ -8,6 +8,7 @@ #include <assetFactory/assetFactory.h> #include <game/geoData.h> +#include <game/network/rail.h> #include <game/terrain.h> #include <game/vehicles/railVehicle.h> #include <game/vehicles/railVehicleClass.h> @@ -24,6 +25,7 @@ class TestScene : public SceneProvider { const RailVehicleClassPtr brush47rvc = std::dynamic_pointer_cast<RailVehicleClass>( AssetFactory::loadXML(RESDIR "/brush47.xml")->assets.at("brush-47")); std::shared_ptr<RailVehicle> train1, train2; + RailLinks rail; Terrain terrain {[]() { auto gd = std::make_shared<GeoData>(GeoData::createFlat({0, 0}, {1000000, 1000000}, 1)); @@ -41,6 +43,8 @@ public: train2->location.setPosition({52000, 30000, 2000}); train2->bogies.front().setPosition(train2->bogies.front().position() + train2->location.position()); train2->bogies.back().setPosition(train2->bogies.back().position() + train2->location.position()); + rail.addLinksBetween({42000, 50000, 1000}, {65000, 50000, 1000}); + rail.addLinksBetween({65000, 50000, 1000}, {75000, 45000, 2000}); } void @@ -48,6 +52,7 @@ public: { terrain.render(shader); brush47rvc->render(shader); + rail.render(shader); } void @@ -158,4 +163,50 @@ BOOST_AUTO_TEST_CASE(terrain) Texture::save(outImage, "/tmp/terrain.tga"); } +BOOST_AUTO_TEST_CASE(railnet) +{ + SceneRenderer ss {size, output}; + ss.camera.setView({0, 0, 10000}, glm::normalize(glm::vec3 {1, 1, -0.5F})); + + class TestRail : public SceneProvider { + RailLinks net; + + public: + TestRail() + { + net.addLinksBetween({20000, 10000, 0}, {100000, 100000, 0}); + net.addLinksBetween({20000, 10000, 0}, {10000, 10000, 0}); + net.addLinksBetween({10000, 20000, 0}, {100000, 120000, 0}); + net.addLinksBetween({10000, 20000, 0}, {10000, 10000, 0}); + net.addLinksBetween({100000, 100000, 0}, {100000, 120000, 0}); + } + + void + content(const SceneShader & shader) const override + { + net.render(shader); + } + + void + environment(const SceneShader &, const SceneRenderer & sr) const override + { + sr.setAmbientLight({0.1, 0.1, 0.1}); + sr.setDirectionalLight({1, 1, 1}, south + down, *this); + } + + void + lights(const SceneShader &) const override + { + } + + void + shadows(const ShadowMapper &) const override + { + } + }; + + ss.render(TestRail {}); + Texture::save(outImage, "/tmp/railnet.tga"); +} + BOOST_AUTO_TEST_SUITE_END(); diff --git a/thirdparty/ctre b/thirdparty/ctre new file mode 160000 +Subproject b3d7788b559e34d985c8530c3e0e7260b67505a |