summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Goodliffe <dan@randomdan.homeip.net>2024-01-24 00:46:57 +0000
committerDan Goodliffe <dan@randomdan.homeip.net>2024-01-24 00:46:57 +0000
commit17aa6053e641d006649f94bb60a834ce945f351e (patch)
treeb76f6fc642b749ee69d4be4b2e3f60e80c0ed7bc
parentAdd traits wrapper for setting uniforms (diff)
parentFix network population of position in gBuffer (diff)
downloadilt-17aa6053e641d006649f94bb60a834ce945f351e.tar.bz2
ilt-17aa6053e641d006649f94bb60a834ce945f351e.tar.xz
ilt-17aa6053e641d006649f94bb60a834ce945f351e.zip
Merge remote-tracking branch 'origin/instanced-networks'
-rw-r--r--.gitmodules3
-rw-r--r--game/network/network.h19
-rw-r--r--game/network/network.impl.h47
-rw-r--r--game/network/rail.cpp154
-rw-r--r--game/network/rail.h39
-rw-r--r--gfx/gl/sceneShader.cpp34
-rw-r--r--gfx/gl/sceneShader.h13
-rw-r--r--gfx/gl/shader.cpp37
-rw-r--r--gfx/gl/shader.h11
-rw-r--r--gfx/gl/shaders/network.fs17
-rw-r--r--gfx/gl/shaders/networkCommon.glsl43
-rw-r--r--gfx/gl/shaders/networkCurve.gs47
-rw-r--r--gfx/gl/shaders/networkCurve.vs29
-rw-r--r--gfx/gl/shaders/networkStraight.gs17
-rw-r--r--gfx/gl/shaders/networkStraight.vs24
-rw-r--r--lib/embed-glsl.cpp.m49
-rw-r--r--lib/gl_traits.h9
-rw-r--r--lib/strlen.h14
-rw-r--r--test/test-glContextBhvr.cpp3
-rw-r--r--test/test-network.cpp47
-rw-r--r--test/test-render.cpp51
m---------thirdparty/ctre0
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