summaryrefslogtreecommitdiff
path: root/game
diff options
context:
space:
mode:
Diffstat (limited to 'game')
-rw-r--r--game/environment.cpp74
-rw-r--r--game/environment.h11
-rw-r--r--game/geoData.cpp30
-rw-r--r--game/mixins/lights.cpp30
-rw-r--r--game/mixins/lights.h26
-rw-r--r--game/network/link.cpp65
-rw-r--r--game/network/link.h7
-rw-r--r--game/network/network.cpp83
-rw-r--r--game/network/network.h28
-rw-r--r--game/network/network.impl.h37
-rw-r--r--game/network/rail.cpp110
-rw-r--r--game/scenary/foliage.cpp143
-rw-r--r--game/scenary/foliage.h29
-rw-r--r--game/scenary/illuminator.cpp71
-rw-r--r--game/scenary/illuminator.h44
-rw-r--r--game/scenary/light.cpp4
-rw-r--r--game/scenary/light.h7
-rw-r--r--game/scenary/plant.cpp2
-rw-r--r--game/scenary/plant.h2
-rw-r--r--game/terrain.cpp47
-rw-r--r--game/terrain.h6
-rw-r--r--game/vehicles/railVehicle.cpp59
-rw-r--r--game/vehicles/railVehicle.h10
-rw-r--r--game/vehicles/railVehicleClass.cpp55
-rw-r--r--game/vehicles/railVehicleClass.h14
-rw-r--r--game/vehicles/train.cpp6
-rw-r--r--game/vehicles/train.h7
-rw-r--r--game/vehicles/vehicle.h2
-rw-r--r--game/water.cpp13
29 files changed, 602 insertions, 420 deletions
diff --git a/game/environment.cpp b/game/environment.cpp
index acb4f21..58a5b53 100644
--- a/game/environment.cpp
+++ b/game/environment.cpp
@@ -3,23 +3,41 @@
#include <chronology.h>
#include <gfx/gl/sceneRenderer.h>
-Environment::Environment() : worldTime {"2024-01-01T12:00:00"_time_t} { }
+constexpr Direction2D DONCASTER = {-1.1_degrees, 53.5_degrees};
+
+Environment::Environment() : worldTime {"2026-06-01T12:00:00"_seconds}, gameTimeScaleFactor {1440}, earthPos {DONCASTER}
+{
+}
void
-Environment::tick(TickDuration)
+Environment::tick(TickDuration elapsed)
+{
+ worldTime += std::chrono::duration_cast<WorldTime::duration>(elapsed * gameTimeScaleFactor);
+}
+
+Environment::WorldTime
+Environment::getWorldTime() const
+{
+ return worldTime;
+}
+
+Direction2D
+Environment::getSunPos() const
{
- worldTime += 50;
+ return getSunPos(earthPos, worldTime.time_since_epoch().count());
}
void
Environment::render(const SceneRenderer & renderer, const SceneProvider & scene) const
{
- constexpr RGB baseAmbient {0.1F}, baseDirectional {0.0F};
- constexpr RGB relativeAmbient {0.3F, 0.3F, 0.4F}, relativeDirectional {0.6F, 0.6F, 0.5F};
+ constexpr RGB SUN_LIGHT {1, 1, .878F};
+ constexpr RGB SKY_BLUE {.529F, .808F, .922F};
+ constexpr RGB BASE_AMBIENT_LIGHT {0.1F};
- const LightDirection sunPos = getSunPos({}, worldTime);
- const auto ambient = baseAmbient + relativeAmbient * sunPos.ambient();
- const auto directional = baseDirectional + relativeDirectional * sunPos.directional();
+ const LightDirection sunPos {getSunPos()};
+ const auto scattered = SKY_BLUE * sunPos.atmosphericScattering() * sunPos.ambient();
+ const auto ambient = BASE_AMBIENT_LIGHT + scattered;
+ const auto directional = (SUN_LIGHT - BASE_AMBIENT_LIGHT - scattered) * sunPos.directional();
renderer.setAmbientLight(ambient);
renderer.setDirectionalLight(directional, sunPos, scene);
@@ -39,49 +57,49 @@ Environment::getSunPos(const Direction2D position, const time_t time)
using std::floor;
using std::sin;
using std::tan;
- static const auto JD2451545 = "2000-01-01T12:00:00"_time_t;
+ static const auto jD2451545 = "2000-01-01T12:00:00"_time_t;
// Calculate difference in days between the current Julian Day
// and JD 2451545.0, which is noon 1 January 2000 Universal Time
// Calculate time of the day in UT decimal hours
const auto dDecimalHours = static_cast<float>(time % 86400) / 3600.F;
- const auto dElapsedJulianDays = static_cast<float>(time - JD2451545) / 86400.F;
+ const auto dElapsedJulianDays = static_cast<float>(time - jD2451545) / 86400.F;
// Calculate ecliptic coordinates (ecliptic longitude and obliquity of the
// ecliptic in radians but without limiting the angle to be less than 2*Pi
// (i.e., the result may be greater than 2*Pi)
- const auto dOmega = 2.1429F - 0.0010394594F * dElapsedJulianDays;
- const auto dMeanLongitude = 4.8950630F + 0.017202791698F * dElapsedJulianDays; // Radians
- const auto dMeanAnomaly = 6.2400600F + 0.0172019699F * dElapsedJulianDays;
- const auto dEclipticLongitude = dMeanLongitude + 0.03341607F * sin(dMeanAnomaly)
- + 0.00034894F * sin(2 * dMeanAnomaly) - 0.0001134F - 0.0000203F * sin(dOmega);
- const auto dEclipticObliquity = 0.4090928F - 6.2140e-9F * dElapsedJulianDays + 0.0000396F * cos(dOmega);
+ const auto dOmega = 2.1429F - (0.0010394594F * dElapsedJulianDays);
+ const auto dMeanLongitude = 4.8950630F + (0.017202791698F * dElapsedJulianDays); // Radians
+ const auto dMeanAnomaly = 6.2400600F + (0.0172019699F * dElapsedJulianDays);
+ const auto dEclipticLongitude = dMeanLongitude + (0.03341607F * sin(dMeanAnomaly))
+ + (0.00034894F * sin(2 * dMeanAnomaly)) - 0.0001134F - (0.0000203F * sin(dOmega));
+ const auto dEclipticObliquity = 0.4090928F - (6.2140e-9F * dElapsedJulianDays) + (0.0000396F * cos(dOmega));
// Calculate celestial coordinates ( right ascension and declination ) in radians
// but without limiting the angle to be less than 2*Pi (i.e., the result may be
// greater than 2*Pi)
- const auto dSin_EclipticLongitude = sin(dEclipticLongitude);
- const auto dY = cos(dEclipticObliquity) * dSin_EclipticLongitude;
- const auto dX = cos(dEclipticLongitude);
- auto dRightAscension = atan2(dY, dX);
+ const auto dSinEclipticLongitude = sin(dEclipticLongitude);
+ const auto decY = cos(dEclipticObliquity) * dSinEclipticLongitude;
+ const auto decX = cos(dEclipticLongitude);
+ auto dRightAscension = atan2(decY, decX);
if (dRightAscension < 0) {
dRightAscension = dRightAscension + two_pi;
}
- const auto dDeclination = asin(sin(dEclipticObliquity) * dSin_EclipticLongitude);
+ const auto dDeclination = asin(sin(dEclipticObliquity) * dSinEclipticLongitude);
// Calculate local coordinates ( azimuth and zenith angle ) in degrees
- const auto dGreenwichMeanSiderealTime = 6.6974243242F + 0.0657098283F * dElapsedJulianDays + dDecimalHours;
+ const auto dGreenwichMeanSiderealTime = 6.6974243242F + (0.0657098283F * dElapsedJulianDays) + dDecimalHours;
const auto dLocalMeanSiderealTime
- = (dGreenwichMeanSiderealTime * 15.0F + (longitude / degreesToRads)) * degreesToRads;
+ = ((dGreenwichMeanSiderealTime * 15.0F) + (longitude / degreesToRads)) * degreesToRads;
const auto dHourAngle = dLocalMeanSiderealTime - dRightAscension;
const auto dLatitudeInRadians = latitude;
- const auto dCos_Latitude = cos(dLatitudeInRadians);
- const auto dSin_Latitude = sin(dLatitudeInRadians);
- const auto dCos_HourAngle = cos(dHourAngle);
+ const auto dCosLatitude = cos(dLatitudeInRadians);
+ const auto dSinLatitude = sin(dLatitudeInRadians);
+ const auto dCosHourAngle = cos(dHourAngle);
Direction2D udtSunCoordinates;
udtSunCoordinates.y
- = (acos(dCos_Latitude * dCos_HourAngle * cos(dDeclination) + sin(dDeclination) * dSin_Latitude));
- udtSunCoordinates.x = atan2(-sin(dHourAngle), tan(dDeclination) * dCos_Latitude - dSin_Latitude * dCos_HourAngle);
+ = (acos((dCosLatitude * dCosHourAngle * cos(dDeclination)) + (sin(dDeclination) * dSinLatitude)));
+ udtSunCoordinates.x = atan2(-sin(dHourAngle), (tan(dDeclination) * dCosLatitude) - (dSinLatitude * dCosHourAngle));
if (udtSunCoordinates.x < 0) {
udtSunCoordinates.x = udtSunCoordinates.x + two_pi;
}
diff --git a/game/environment.h b/game/environment.h
index a6f3036..94211bc 100644
--- a/game/environment.h
+++ b/game/environment.h
@@ -2,17 +2,24 @@
#include "config/types.h"
#include "worldobject.h"
+#include <chrono>
class SceneRenderer;
class SceneProvider;
class Environment : public WorldObject {
public:
+ using WorldTime = std::chrono::utc_time<std::chrono::seconds>;
+
Environment();
void tick(TickDuration elapsed) override;
void render(const SceneRenderer &, const SceneProvider &) const;
- static Direction2D getSunPos(const Direction2D position, const time_t time);
+ [[nodiscard]] Direction2D getSunPos() const;
+ [[nodiscard]] WorldTime getWorldTime() const;
+ [[nodiscard]] static Direction2D getSunPos(Direction2D position, time_t time);
private:
- time_t worldTime;
+ WorldTime worldTime;
+ uint16_t gameTimeScaleFactor;
+ glm::vec<2, Angle> earthPos;
};
diff --git a/game/geoData.cpp b/game/geoData.cpp
index 5cea4dd..b886efd 100644
--- a/game/geoData.cpp
+++ b/game/geoData.cpp
@@ -121,11 +121,9 @@ GeoData::intersectRay(const Ray<GlobalPosition3D> & ray, FaceHandle face) const
walkUntil(PointFace {ray.start, face},
ray.start.xy() + (ray.direction.xy() * ::difference(extents.max.xy(), extents.min.xy())),
[&out, &ray, this](const auto & step) {
- BaryPosition bari {};
- RelativeDistance dist {};
const auto t = triangle<3>(step.current);
- if (ray.intersectTriangle(t.x, t.y, t.z, bari, dist)) {
- out.emplace(t * bari, step.current);
+ if (const auto inter = ray.intersectTriangle(t.x, t.y, t.z)) {
+ out.emplace(t * inter->bary, step.current);
return true;
}
return false;
@@ -286,9 +284,27 @@ GeoData::updateAllVertexNormals(const R & range)
void
GeoData::updateVertexNormal(VertexHandle vertex)
{
- Normal3D n;
- calc_vertex_normal_correct(vertex, n);
- set_normal(vertex, glm::normalize(n));
+ Normal3D normal {};
+ { // Lifted from calc_vertex_normal_correct in PolyMeshT_impl.hh but doesn't use Scalar to normalise
+ ConstVertexIHalfedgeIter cvih_it = this->cvih_iter(vertex);
+ if (!cvih_it.is_valid()) { // don't crash on isolated vertices
+ return;
+ }
+ Normal in_he_vec;
+ calc_edge_vector(*cvih_it, in_he_vec);
+ for (; cvih_it.is_valid(); ++cvih_it) { // calculates the sector normal defined by cvih_it and adds it to normal
+ if (this->is_boundary(*cvih_it)) {
+ continue;
+ }
+ HalfedgeHandle out_heh(this->next_halfedge_handle(*cvih_it));
+ Normal out_he_vec;
+ calc_edge_vector(out_heh, out_he_vec);
+ normal += cross(in_he_vec, out_he_vec); // sector area is taken into account
+ in_he_vec = out_he_vec;
+ in_he_vec *= -1; // change the orientation
+ }
+ } // End lift
+ set_normal(vertex, glm::normalize(normal));
}
OpenMesh::VertexHandle
diff --git a/game/mixins/lights.cpp b/game/mixins/lights.cpp
new file mode 100644
index 0000000..6829bbb
--- /dev/null
+++ b/game/mixins/lights.cpp
@@ -0,0 +1,30 @@
+#include "lights.h"
+#include "gfx/renderable.h"
+
+bool
+AssetLights::persist(Persistence::PersistenceStore & store)
+{
+ return STORE_HELPER(pointLight, Persistence::Appender<decltype(pointLight)>)
+ && STORE_HELPER(spotLight, Persistence::Appender<decltype(spotLight)>);
+}
+
+void
+InstanceLights::lightsEnable(AnyPtr<const AssetLights> asset, uint32_t owner)
+{
+ auto createLights = [owner](const auto & assetLights, auto & lightInstances, auto commonLights) {
+ std::ranges::transform(assetLights | std::views::enumerate, std::inserter(lightInstances, lightInstances.end()),
+ [&commonLights, owner](const auto & idxAndLight) {
+ const auto & [idx, light] = idxAndLight;
+ return std::make_pair(idx, commonLights->acquire(*light, owner));
+ });
+ };
+ createLights(asset->spotLight, spotLightInstances, Renderable::commonSpotLights.lock());
+ createLights(asset->pointLight, pointLightInstances, Renderable::commonPointLights.lock());
+}
+
+void
+InstanceLights::lightsDisable()
+{
+ spotLightInstances.clear();
+ pointLightInstances.clear();
+}
diff --git a/game/mixins/lights.h b/game/mixins/lights.h
new file mode 100644
index 0000000..c2207a5
--- /dev/null
+++ b/game/mixins/lights.h
@@ -0,0 +1,26 @@
+#pragma once
+
+#include "assetFactory/lights.h"
+#include "gfx/gl/instanceVertices.h"
+#include <flat_map>
+
+class AssetLights {
+protected:
+ bool persist(Persistence::PersistenceStore & store);
+ template<typename T> using LightVec = std::vector<typename T::Ptr>;
+
+ LightVec<SpotLight> spotLight;
+ LightVec<PointLight> pointLight;
+
+ friend class InstanceLights;
+};
+
+class InstanceLights {
+protected:
+ template<typename V> using LightInstanceMap = std::flat_map<size_t, typename InstanceVertices<V>::InstanceProxy>;
+ LightInstanceMap<SpotLightVertex> spotLightInstances;
+ LightInstanceMap<PointLightVertex> pointLightInstances;
+
+ void lightsEnable(AnyPtr<const AssetLights>, uint32_t);
+ void lightsDisable();
+};
diff --git a/game/network/link.cpp b/game/network/link.cpp
index c84524c..b8ffee2 100644
--- a/game/network/link.cpp
+++ b/game/network/link.cpp
@@ -5,37 +5,43 @@
#include <ray.h>
#include <tuple>
-Link::Link(End a, End b, float l) : ends {{std::move(a), std::move(b)}}, length {l} { }
+Link::Link(End endA, End endB, float len) : ends {{std::move(endA), std::move(endB)}}, length {len} { }
-LinkCurve::LinkCurve(GlobalPosition3D c, RelativeDistance r, Arc a) : centreBase {c}, radius {r}, arc {std::move(a)} { }
+LinkCurve::LinkCurve(GlobalPosition3D centre, RelativeDistance radius, Arc arc) :
+ centreBase {centre}, radius {radius}, arc {std::move(arc)}
+{
+}
bool
-operator<(const GlobalPosition3D & a, const GlobalPosition3D & b)
+operator<(const GlobalPosition3D & left, const GlobalPosition3D & right)
{
// NOLINTNEXTLINE(hicpp-use-nullptr,modernize-use-nullptr)
- return std::tie(a.x, a.y, a.z) < std::tie(b.x, b.y, b.z);
+ return std::tie(left.x, left.y, left.z) < std::tie(right.x, right.y, right.z);
}
bool
-operator<(const Node & a, const Node & b)
+operator<(const Node & left, const Node & right)
{
- return a.pos < b.pos;
+ return left.pos < right.pos;
}
Location
LinkStraight::positionAt(RelativeDistance dist, unsigned char start) const
{
- const auto es {std::make_pair(ends[start].node.get(), ends[1 - start].node.get())};
- const RelativePosition3D diff {es.second->pos - es.first->pos};
- const auto dir {glm::normalize(diff)};
- return Location {es.first->pos + (vehiclePositionOffset() + dir * dist), {vector_pitch(dir), vector_yaw(dir), 0}};
+ const auto endNodes = std::make_pair(ends[start].node.get(), ends[1 - start].node.get());
+ const auto diff = ::difference(endNodes.second->pos, endNodes.first->pos);
+ const auto directionVector = glm::normalize(diff);
+ return Location {
+ .pos = endNodes.first->pos + (vehiclePositionOffset() + directionVector * dist),
+ .rot = {vector_pitch(directionVector), vector_yaw(directionVector), 0},
+ };
}
bool
LinkStraight::intersectRay(const Ray<GlobalPosition3D> & ray) const
{
- return ray.passesCloseToEdges(
- std::array {GlobalPosition3D {ends.front().node->pos}, GlobalPosition3D {ends.back().node->pos}}, 1000);
+ static constexpr auto PROXIMITY = 1'000;
+ return ray.passesCloseToEdges(std::array {ends.front().node->pos, ends.back().node->pos}, PROXIMITY);
}
std::vector<GlobalPosition3D>
@@ -55,33 +61,36 @@ LinkStraight::getBase(RelativeDistance width) const
Location
LinkCurve::positionAt(float dist, unsigned char start) const
{
- static constexpr std::array<float, 2> dirOffset {half_pi, -half_pi};
- const auto frac {dist / length};
- const auto es {std::make_pair(ends[start].node.get(), ends[1 - start].node.get())};
- const auto as {std::make_pair(arc[start], arc[1 - start])};
- const auto ang {as.first + ((as.second - as.first) * frac)};
- const auto relPos {(sincos(ang) || 0.F) * radius};
- const auto relClimb {vehiclePositionOffset()
+ static constexpr std::array DIR_OFFSET {half_pi, -half_pi};
+ const auto frac = dist / length;
+ const auto endNodes = std::make_pair(ends[start].node.get(), ends[1 - start].node.get());
+ const auto arcEndAngles = std::make_pair(arc[start], arc[1 - start]);
+ const auto ang = glm::mix(arcEndAngles.first, arcEndAngles.second, frac);
+ const auto relPos = (sincos(ang) || 0.F) * radius;
+ const auto relClimb = vehiclePositionOffset()
+ RelativePosition3D {0, 0,
- static_cast<RelativeDistance>(es.first->pos.z - centreBase.z)
- + (static_cast<RelativeDistance>(es.second->pos.z - es.first->pos.z) * frac)}};
- const auto pitch {vector_pitch(difference(es.second->pos, es.first->pos) / length)};
- return Location {GlobalPosition3D(relPos + relClimb) + centreBase, {pitch, normalize(ang + dirOffset[start]), 0}};
+ static_cast<RelativeDistance>(endNodes.first->pos.z - centreBase.z)
+ + (static_cast<RelativeDistance>(endNodes.second->pos.z - endNodes.first->pos.z) * frac)};
+ const auto pitch {vector_pitch(difference(endNodes.second->pos, endNodes.first->pos) / length)};
+ return Location {
+ .pos = GlobalPosition3D(relPos + relClimb) + centreBase,
+ .rot = {pitch, normalize(ang + DIR_OFFSET[start]), 0},
+ };
}
bool
LinkCurve::intersectRay(const Ray<GlobalPosition3D> & ray) const
{
- const auto & e0p {ends[0].node->pos};
- const auto & e1p {ends[1].node->pos};
+ const auto e0p = ends[0].node->pos.z;
+ const auto e1p = ends[1].node->pos.z;
const auto slength = round_frac(length / 2.F, 5.F);
const auto segs = std::round(15.F * slength / std::pow(radius, 0.7F));
- const auto step {glm::vec<2, RelativeDistance> {arc.length(), e1p.z - e0p.z} / segs};
+ const auto step {glm::vec<2, RelativeDistance> {arc.length(), e1p - e0p} / segs};
auto segCount = static_cast<std::size_t>(std::lround(segs)) + 1;
std::vector<GlobalPosition3D> points;
points.reserve(segCount);
- for (std::remove_const_t<decltype(step)> swing = {arc.first, centreBase.z - e0p.z}; segCount;
+ for (std::remove_const_t<decltype(step)> swing = {arc.first, centreBase.z - e0p}; segCount;
swing += step, --segCount) {
points.emplace_back(centreBase + ((sincos(swing.x) * radius) || swing.y));
}
@@ -94,7 +103,7 @@ LinkCurve::getBase(RelativeDistance width) const
const auto start = ends.front().node->pos;
const auto end = ends.back().node->pos;
const auto segs = std::ceil(std::sqrt(radius) * 0.02F * arc.length());
- const auto step {glm::vec<2, RelativeDistance> {arc.length(), end.z - start.z} / segs};
+ const auto step = glm::vec<2, RelativeDistance> {arc.length(), end.z - start.z} / segs;
auto segCount = static_cast<size_t>(segs) + 1;
std::vector<GlobalPosition3D> out;
diff --git a/game/network/link.h b/game/network/link.h
index 59bbb65..0b58558 100644
--- a/game/network/link.h
+++ b/game/network/link.h
@@ -16,7 +16,7 @@ template<typename> class Ray;
// it has location
class Node : public StdTypeDefs<Node> {
public:
- explicit Node(GlobalPosition3D p) noexcept : pos(p) { };
+ explicit Node(GlobalPosition3D position) noexcept : pos(position) { };
virtual ~Node() noexcept = default;
NO_COPY(Node);
NO_MOVE(Node);
@@ -35,6 +35,7 @@ public:
struct End {
Node::Ptr node;
float dir;
+ // NOLINTNEXTLINE(readability-redundant-member-init) don't require client to empty initialise this
Nexts nexts {};
};
@@ -58,8 +59,8 @@ protected:
}
};
-bool operator<(const GlobalPosition3D & a, const GlobalPosition3D & b);
-bool operator<(const Node & a, const Node & b);
+bool operator<(const GlobalPosition3D &, const GlobalPosition3D &);
+bool operator<(const Node &, const Node &);
class LinkStraight : public virtual Link {
public:
diff --git a/game/network/network.cpp b/game/network/network.cpp
index e67942f..c8482de 100644
--- a/game/network/network.cpp
+++ b/game/network/network.cpp
@@ -8,8 +8,8 @@
#include <stdexcept>
#include <utility>
-Network::Network(const std::string & tn) :
- texture {std::make_shared<Texture>(tn,
+Network::Network(const std::string & textureName) :
+ texture {std::make_shared<Texture>(textureName,
TextureOptions {
.minFilter = GL_NEAREST_MIPMAP_LINEAR,
})}
@@ -25,19 +25,18 @@ Network::nodeAt(GlobalPosition3D pos)
Network::NodeInsertion
Network::newNodeAt(GlobalPosition3D pos)
{
- if (auto [n, i] = candidateNodeAt(pos); i == NodeIs::NotInNetwork) {
- return {*nodes.insert(std::move(n)).first, i};
- }
- else {
- return {std::move(n), NodeIs::InNetwork};
+ auto [node, inNetwork] = candidateNodeAt(pos);
+ if (inNetwork == NodeIs::NotInNetwork) {
+ return {*nodes.insert(std::move(node)).first, inNetwork};
}
+ return {std::move(node), NodeIs::InNetwork};
}
Node::Ptr
Network::findNodeAt(GlobalPosition3D pos) const
{
- if (const auto n = nodes.find(pos); n != nodes.end()) {
- return *n;
+ if (const auto node = nodes.find(pos); node != nodes.end()) {
+ return *node;
}
return {};
}
@@ -45,8 +44,8 @@ Network::findNodeAt(GlobalPosition3D pos) const
Network::NodeInsertion
Network::candidateNodeAt(GlobalPosition3D pos) const
{
- if (const auto n = nodes.find(pos); n != nodes.end()) {
- return {*n, NodeIs::InNetwork};
+ if (const auto node = nodes.find(pos); node != nodes.end()) {
+ return {*node, NodeIs::InNetwork};
}
return {std::make_shared<Node>(pos), NodeIs::NotInNetwork};
}
@@ -54,12 +53,11 @@ Network::candidateNodeAt(GlobalPosition3D pos) const
Node::Ptr
Network::intersectRayNodes(const Ray<GlobalPosition3D> & ray) const
{
+ static constexpr auto MIN_DISTANCE = 2000;
// Click within 2m of a node
if (const auto node = std::find_if(nodes.begin(), nodes.end(),
[&ray](const Node::Ptr & node) {
- GlobalPosition3D ipos;
- Normal3D inorm;
- return ray.intersectSphere(node->pos, 2000, ipos, inorm);
+ return ray.intersectSphere(node->pos, MIN_DISTANCE);
});
node != nodes.end()) {
return *node;
@@ -68,14 +66,14 @@ Network::intersectRayNodes(const Ray<GlobalPosition3D> & ray) const
}
void
-Network::joinLinks(const Link::Ptr & l, const Link::Ptr & ol)
+Network::joinLinks(const Link::Ptr & link, const Link::Ptr & oldLink)
{
- if (l != ol) {
- for (const auto oe : {0U, 1U}) {
- for (const auto te : {0U, 1U}) {
- if (l->ends[te].node == ol->ends[oe].node) {
- l->ends[te].nexts.emplace_back(ol, oe);
- ol->ends[oe].nexts.emplace_back(l, te);
+ if (link != oldLink) {
+ for (const auto oldLinkEnd : {0U, 1U}) {
+ for (const auto linkEnd : {0U, 1U}) {
+ if (link->ends[linkEnd].node == oldLink->ends[oldLinkEnd].node) {
+ link->ends[linkEnd].nexts.emplace_back(oldLink, oldLinkEnd);
+ oldLink->ends[oldLinkEnd].nexts.emplace_back(link, linkEnd);
}
}
}
@@ -93,7 +91,7 @@ Network::routeFromTo(const Link::End & start, GlobalPosition3D dest) const
}
Link::Nexts
-Network::routeFromTo(const Link::End & end, const Node::Ptr & dest) const
+Network::routeFromTo(const Link::End & end, const Node::Ptr & dest)
{
return RouteWalker().findRouteTo(end, dest);
}
@@ -102,11 +100,11 @@ GenCurveDef
Network::genCurveDef(const GlobalPosition3D & start, const GlobalPosition3D & end, float startDir)
{
const auto diff = difference(end, start);
- const auto vy {vector_yaw(diff)};
+ const auto yaw = vector_yaw(diff);
const auto dir = pi + startDir;
- const auto flatStart {start.xy()}, flatEnd {end.xy()};
- const auto n2ed {(vy * 2) - dir - pi};
- const auto centre {find_arc_centre(flatStart, dir, flatEnd, n2ed)};
+ const auto flatStart = start.xy(), flatEnd = end.xy();
+ const auto n2ed = (yaw * 2) - dir - pi;
+ const auto centre = find_arc_centre(flatStart, dir, flatEnd, n2ed);
if (centre.second) { // right hand arc
return {end, start, centre.first};
@@ -121,24 +119,23 @@ Network::genCurveDef(const GlobalPosition3D & start, const GlobalPosition3D & en
endDir += pi;
const auto flatStart {start.xy()}, flatEnd {end.xy()};
auto midheight = [&](auto mid) {
- const auto sm = ::distance<2>(flatStart, mid);
- const auto em = ::distance<2>(flatEnd, mid);
- return start.z + GlobalDistance(RelativeDistance(end.z - start.z) * (sm / (sm + em)));
+ const auto startToMid = ::distance<2>(flatStart, mid);
+ const auto endToMid = ::distance<2>(flatEnd, mid);
+ return start.z + GlobalDistance(RelativeDistance(end.z - start.z) * (startToMid / (startToMid + endToMid)));
};
- if (const auto radii = find_arcs_radius(flatStart, startDir, flatEnd, endDir); radii.first < radii.second) {
- const auto radius {radii.first};
- const auto c1 = flatStart + (sincos(startDir + half_pi) * radius);
- const auto c2 = flatEnd + (sincos(endDir + half_pi) * radius);
- const auto mid = (c1 + c2) / 2;
- const auto midh = mid || midheight(mid);
- return {{start, midh, c1}, {end, midh, c2}};
- }
- else {
- const auto radius {radii.second};
- const auto c1 = flatStart + (sincos(startDir - half_pi) * radius);
- const auto c2 = flatEnd + (sincos(endDir - half_pi) * radius);
- const auto mid = (c1 + c2) / 2;
+ const auto radii = find_arcs_radius(flatStart, startDir, flatEnd, endDir);
+ if (radii.first < radii.second) {
+ const auto radius = radii.first;
+ const auto centre1 = flatStart + (sincos(startDir + half_pi) * radius);
+ const auto centre2 = flatEnd + (sincos(endDir + half_pi) * radius);
+ const auto mid = (centre1 + centre2) / 2;
const auto midh = mid || midheight(mid);
- return {{midh, start, c1}, {midh, end, c2}};
+ return {{start, midh, centre1}, {end, midh, centre2}};
}
+ const auto radius = radii.second;
+ const auto centre1 = flatStart + (sincos(startDir - half_pi) * radius);
+ const auto centre2 = flatEnd + (sincos(endDir - half_pi) * radius);
+ const auto mid = (centre1 + centre2) / 2;
+ const auto midh = mid || midheight(mid);
+ return {{midh, start, centre1}, {midh, end, centre2}};
}
diff --git a/game/network/network.h b/game/network/network.h
index 73c3788..46b84d4 100644
--- a/game/network/network.h
+++ b/game/network/network.h
@@ -1,6 +1,7 @@
#pragma once
#include "collection.h"
+#include "gfx/gl/glVertexArray.h"
#include "gfx/gl/instanceVertices.h"
#include "gfx/models/texture.h"
#include "gfx/renderable.h"
@@ -18,7 +19,7 @@ struct Surface;
class GeoData;
template<typename> class Ray;
-template<size_t... n> using GenDef = std::tuple<glm::vec<n, GlobalDistance>...>;
+template<size_t... N> using GenDef = std::tuple<glm::vec<N, GlobalDistance>...>;
using GenCurveDef = GenDef<3, 3, 2>;
class Network {
@@ -30,7 +31,7 @@ public:
[[nodiscard]] Node::Ptr findNodeAt(GlobalPosition3D) const;
[[nodiscard]] Node::Ptr nodeAt(GlobalPosition3D);
- enum class NodeIs { InNetwork, NotInNetwork };
+ enum class NodeIs : uint8_t { InNetwork, NotInNetwork };
using NodeInsertion = std::pair<Node::Ptr, NodeIs>;
[[nodiscard]] NodeInsertion newNodeAt(GlobalPosition3D);
[[nodiscard]] NodeInsertion candidateNodeAt(GlobalPosition3D) const;
@@ -38,7 +39,7 @@ public:
[[nodiscard]] virtual Node::Ptr intersectRayNodes(const Ray<GlobalPosition3D> &) const;
[[nodiscard]] Link::Nexts routeFromTo(const Link::End &, GlobalPosition3D) const;
- [[nodiscard]] Link::Nexts routeFromTo(const Link::End &, const Node::Ptr &) const;
+ [[nodiscard]] static Link::Nexts routeFromTo(const Link::End &, const Node::Ptr &);
virtual Link::CCollection candidateStraight(GlobalPosition3D, GlobalPosition3D) = 0;
virtual Link::CCollection candidateJoins(GlobalPosition3D, GlobalPosition3D) = 0;
@@ -53,7 +54,7 @@ public:
[[nodiscard]] virtual RelativeDistance getBaseWidth() const = 0;
protected:
- static void joinLinks(const Link::Ptr & l, const Link::Ptr & ol);
+ static void joinLinks(const Link::Ptr & link, const Link::Ptr & oldLink);
static GenCurveDef genCurveDef(const GlobalPosition3D & start, const GlobalPosition3D & end, float startDir);
static std::pair<GenCurveDef, GenCurveDef> genCurveDef(
const GlobalPosition3D & start, const GlobalPosition3D & end, float startDir, float endDir);
@@ -80,34 +81,33 @@ protected:
SharedCollection<T> links;
void joinLinks(const Link::Ptr &) const;
-protected:
[[nodiscard]] Link::Ptr intersectRayLinks(const Ray<GlobalPosition3D> &) const override;
public:
template<typename L, typename... Params>
std::shared_ptr<L>
- candidateLink(GlobalPosition3D a, GlobalPosition3D b, Params &&... params)
+ candidateLink(GlobalPosition3D positionA, GlobalPosition3D positionB, Params &&... params)
requires std::is_base_of_v<T, L>
{
- const auto node1 = candidateNodeAt(a).first, node2 = candidateNodeAt(b).first;
+ const auto node1 = candidateNodeAt(positionA).first, node2 = candidateNodeAt(positionB).first;
return std::make_shared<L>(*this, node1, node2, std::forward<Params>(params)...);
}
template<typename L, typename... Params>
std::shared_ptr<L>
- addLink(GlobalPosition3D a, GlobalPosition3D b, Params &&... params)
+ addLink(GlobalPosition3D positionA, GlobalPosition3D positionB, Params &&... params)
requires std::is_base_of_v<T, L>
{
- const auto node1 = nodeAt(a), node2 = nodeAt(b);
- auto l {links.template create<L>(*this, node1, node2, std::forward<Params>(params)...)};
- joinLinks(l);
- return l;
+ const auto node1 = nodeAt(positionA), node2 = nodeAt(positionB);
+ auto newLink = links.template create<L>(*this, node1, node2, std::forward<Params>(params)...);
+ joinLinks(newLink);
+ return std::move(newLink);
}
- Link::CCollection candidateStraight(GlobalPosition3D n1, GlobalPosition3D n2) override;
+ Link::CCollection candidateStraight(GlobalPosition3D, GlobalPosition3D) override;
Link::CCollection candidateJoins(GlobalPosition3D, GlobalPosition3D) override;
Link::CCollection candidateExtend(GlobalPosition3D, GlobalPosition3D) override;
- Link::CCollection addStraight(const GeoData *, GlobalPosition3D n1, GlobalPosition3D n2) override;
+ Link::CCollection addStraight(const GeoData *, GlobalPosition3D, GlobalPosition3D) override;
Link::CCollection addJoins(const GeoData *, GlobalPosition3D, GlobalPosition3D) override;
Link::CCollection addExtend(const GeoData *, GlobalPosition3D, GlobalPosition3D) override;
diff --git a/game/network/network.impl.h b/game/network/network.impl.h
index 294f696..e339922 100644
--- a/game/network/network.impl.h
+++ b/game/network/network.impl.h
@@ -6,10 +6,10 @@
template<typename T, typename... Links>
void
-NetworkOf<T, Links...>::joinLinks(const Link::Ptr & l) const
+NetworkOf<T, Links...>::joinLinks(const Link::Ptr & link) const
{
- for (const auto & ol : links) {
- Network::joinLinks(l, ol);
+ for (const auto & oldLink : links) {
+ Network::joinLinks(link, oldLink);
}
}
@@ -32,11 +32,11 @@ template<typename T, typename... Links>
float
NetworkOf<T, Links...>::findNodeDirection(Node::AnyCPtr n) const
{
- for (const auto & l : links) {
- for (const auto & e : l->ends) {
+ for (const auto & link : links) {
+ for (const auto & end : link->ends) {
// cppcheck-suppress useStlAlgorithm
- if (e.node.get() == n.get()) {
- return e.dir;
+ if (end.node.get() == n.get()) {
+ return end.dir;
}
}
}
@@ -45,16 +45,17 @@ NetworkOf<T, Links...>::findNodeDirection(Node::AnyCPtr n) const
template<typename T, typename... Links>
Link::CCollection
-NetworkOf<T, Links...>::candidateStraight(GlobalPosition3D n1, GlobalPosition3D n2)
+NetworkOf<T, Links...>::candidateStraight(GlobalPosition3D positionA, GlobalPosition3D positionB)
{
- return {candidateLink<typename T::StraightLink>(n1, n2)};
+ return {candidateLink<typename T::StraightLink>(positionA, positionB)};
}
template<typename T, typename... Links>
Link::CCollection
NetworkOf<T, Links...>::candidateJoins(GlobalPosition3D start, GlobalPosition3D end)
{
- if (::distance(start, end) < 2000.F) {
+ static constexpr auto MIN_DISTANCE = 2000.F;
+ if (::distance(start, end) < MIN_DISTANCE) {
return {};
}
const auto defs = genCurveDef(
@@ -74,17 +75,17 @@ NetworkOf<T, Links...>::candidateExtend(GlobalPosition3D start, GlobalPosition3D
template<typename T, typename... Links>
Link::CCollection
-NetworkOf<T, Links...>::addStraight(const GeoData * geoData, GlobalPosition3D n1, GlobalPosition3D n2)
+NetworkOf<T, Links...>::addStraight(const GeoData * geoData, GlobalPosition3D positionA, GlobalPosition3D positionB)
{
Link::CCollection out;
- geoData->walk(n1.xy(), n2, [geoData, &out, this, &n1](const GeoData::WalkStep & step) {
+ geoData->walk(positionA.xy(), positionB, [geoData, &out, this, &positionA](const GeoData::WalkStep & step) {
if (step.previous.is_valid() && geoData->getSurface(step.current) != geoData->getSurface(step.previous)) {
const auto surfaceEdgePosition = geoData->positionAt(GeoData::PointFace(step.exitPosition, step.current));
- out.emplace_back(addLink<typename T::StraightLink>(n1, surfaceEdgePosition));
- n1 = surfaceEdgePosition;
+ out.emplace_back(addLink<typename T::StraightLink>(positionA, surfaceEdgePosition));
+ positionA = surfaceEdgePosition;
}
});
- out.emplace_back(addLink<typename T::StraightLink>(n1, n2));
+ out.emplace_back(addLink<typename T::StraightLink>(positionA, positionB));
return out;
}
@@ -92,6 +93,7 @@ template<typename T, typename... Links>
Link::CCollection
NetworkOf<T, Links...>::addCurve(const GeoData * geoData, const GenCurveDef & curve)
{
+ static constexpr auto MIN_DISTANCE = 2000.F;
auto [cstart, cend, centre] = curve;
Link::CCollection out;
std::set<GeoData::WalkStepCurve, SortedBy<&GeoData::WalkStepCurve::angle>> breaks;
@@ -115,7 +117,7 @@ NetworkOf<T, Links...>::addCurve(const GeoData * geoData, const GenCurveDef & cu
|| geoData->positionAt(GeoData::PointFace(step.exitPosition, step.current)).z;
});
points.push_back(cend);
- mergeClose(points, ::distance<3, GlobalDistance>, ::midpoint<3, GlobalDistance>, 2'000.F);
+ mergeClose(points, ::distance<3, GlobalDistance>, ::midpoint<3, GlobalDistance>, MIN_DISTANCE);
std::ranges::transform(points | std::views::pairwise, std::back_inserter(out), [this, centre](const auto pair) {
const auto [a, b] = pair;
return this->addLink<typename T::CurveLink>(a, b, centre);
@@ -127,7 +129,8 @@ template<typename T, typename... Links>
Link::CCollection
NetworkOf<T, Links...>::addJoins(const GeoData * geoData, GlobalPosition3D start, GlobalPosition3D end)
{
- if (::distance(start, end) < 2000.F) {
+ static constexpr auto MIN_DISTANCE = 2000.F;
+ if (::distance(start, end) < MIN_DISTANCE) {
return {};
}
const auto defs = genCurveDef(start, end, findNodeDirection(nodeAt(start)), findNodeDirection(nodeAt(end)));
diff --git a/game/network/rail.cpp b/game/network/rail.cpp
index dfe1dca..e8cc1b6 100644
--- a/game/network/rail.cpp
+++ b/game/network/rail.cpp
@@ -1,10 +1,8 @@
#include "rail.h"
#include "game/gamestate.h"
-#include "game/geoData.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>;
@@ -40,34 +38,33 @@ RailLinks::addLinksBetween(GlobalPosition3D start, GlobalPosition3D end)
const auto flatStart {start.xy()}, flatEnd {end.xy()};
if (node2ins.second == NodeIs::InNetwork) {
auto midheight = [&](auto mid) {
- const auto sm = ::distance<2>(flatStart, mid);
- const auto em = ::distance<2>(flatEnd, mid);
- return start.z + GlobalDistance(RelativeDistance(end.z - start.z) * (sm / (sm + em)));
+ const auto startToMid = ::distance<2>(flatStart, mid);
+ const auto endToMid = ::distance<2>(flatEnd, mid);
+ return start.z + GlobalDistance(RelativeDistance(end.z - start.z) * (startToMid / (startToMid + endToMid)));
};
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 radii = find_arcs_radius(flatStart, dir, flatEnd, dir2);
+ if (radii.first < radii.second) {
+ const auto radius = radii.first;
+ const auto centre1 = flatStart + (sincos(dir + half_pi) * radius);
+ const auto centre2 = flatEnd + (sincos(dir2 + half_pi) * radius);
+ const auto mid = (centre1 + centre2) / 2;
const auto midh = mid || midheight(mid);
- addLink<RailLinkCurve>(start, midh, c1);
- return addLink<RailLinkCurve>(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<RailLinkCurve>(midh, start, c1);
- return addLink<RailLinkCurve>(midh, end, c2);
+ addLink<RailLinkCurve>(start, midh, centre1);
+ return addLink<RailLinkCurve>(end, midh, centre2);
}
+ const auto radius = radii.second;
+ const auto centre1 = flatStart + (sincos(dir - half_pi) * radius);
+ const auto centre2 = flatEnd + (sincos(dir2 - half_pi) * radius);
+ const auto mid = (centre1 + centre2) / 2;
+ const auto midh = mid || midheight(mid);
+ addLink<RailLinkCurve>(midh, start, centre1);
+ return addLink<RailLinkCurve>(midh, end, centre2);
}
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)};
+ const auto yaw = vector_yaw(diff);
+ const auto n2ed = (yaw * 2) - dir - pi;
+ const auto centre = find_arc_centre(flatStart, dir, flatEnd, n2ed);
if (centre.second) { // right hand arc
std::swap(start, end);
@@ -102,31 +99,34 @@ namespace {
}
}
-RailLinkStraight::RailLinkStraight(NetworkLinkHolder<RailLinkStraight> & instances, const Node::Ptr & a,
- const Node::Ptr & b) : RailLinkStraight(instances, a, b, b->pos - a->pos)
+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 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 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 & a, const Node::Ptr & b, GlobalPosition2D c) :
- RailLinkCurve(instances, a, b, c || a->pos.z, ::distance<2>(a->pos.xy(), c), {c, a->pos, b->pos})
+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 & 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,
- roundSleepers(length), half_pi - arc.first, half_pi - arc.second, radius)}
+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)}
{
}
@@ -138,29 +138,32 @@ RailLink::vehiclePositionOffset() const
template<> NetworkLinkHolder<RailLinkStraight>::NetworkLinkHolder()
{
- VertexArrayObject {vao}
+ glDebugScope _ {0};
+ vao.configure()
.addAttribs<RailLinkStraight::Vertex, &RailLinkStraight::Vertex::a, &RailLinkStraight::Vertex::b,
- &RailLinkStraight::Vertex::rotation, &RailLinkStraight::Vertex::textureRepeats>(
- vertices.bufferName());
+ &RailLinkStraight::Vertex::rotation, &RailLinkStraight::Vertex::textureRepeats>(0);
}
template<> NetworkLinkHolder<RailLinkCurve>::NetworkLinkHolder()
{
- VertexArrayObject {vao}
+ glDebugScope _ {0};
+ vao.configure()
.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());
+ &RailLinkCurve::Vertex::bangle, &RailLinkCurve::Vertex::radius>(0);
}
namespace {
template<typename LinkType>
void
- renderType(const NetworkLinkHolder<LinkType> & n, auto & s)
+ renderType(const NetworkLinkHolder<LinkType> & networkLinks, auto & shader, GLenum mode)
{
- if (auto count = n.vertices.size()) {
- s.use(RAIL_CROSS_SECTION, RAIL_TEXTURE_POS);
- glBindVertexArray(n.vao);
- glDrawArrays(GL_POINTS, 0, static_cast<GLsizei>(count));
+ if (auto count = networkLinks.vertices.size()) {
+ auto _ = glDebugScope(networkLinks.vao);
+ shader.use(RAIL_CROSS_SECTION, RAIL_TEXTURE_POS);
+ glBindVertexArray(networkLinks.vao);
+ networkLinks.vao.useBuffer(0, networkLinks.vertices);
+ glDrawArrays(mode, 0, static_cast<GLsizei>(count));
}
};
}
@@ -169,11 +172,13 @@ void
RailLinks::render(const SceneShader & shader, const Frustum &) const
{
if (!links.empty()) {
- texture->bind();
+ glDebugScope _ {0};
+ texture->bind(0);
glEnable(GL_POLYGON_OFFSET_FILL);
glPolygonOffset(-1, 0);
- renderType<RailLinkStraight>(*this, shader.networkStraight);
- renderType<RailLinkCurve>(*this, shader.networkCurve);
+ renderType<RailLinkStraight>(*this, shader.networkStraight, GL_POINTS);
+ glPatchParameteri(GL_PATCH_VERTICES, 1);
+ renderType<RailLinkCurve>(*this, shader.networkCurve, GL_PATCHES);
glDisable(GL_POLYGON_OFFSET_FILL);
glBindVertexArray(0);
}
@@ -188,5 +193,6 @@ RailLinks::getBaseSurface() const
RelativeDistance
RailLinks::getBaseWidth() const
{
- return 5'700;
+ static constexpr auto BASE_WIDTH = 5'700;
+ return BASE_WIDTH;
}
diff --git a/game/scenary/foliage.cpp b/game/scenary/foliage.cpp
index 159a078..f27ac26 100644
--- a/game/scenary/foliage.cpp
+++ b/game/scenary/foliage.cpp
@@ -1,7 +1,34 @@
#include "foliage.h"
+#include "gfx/frustum.h"
+#include "gfx/gl/billboardPainter.h"
#include "gfx/gl/sceneShader.h"
#include "gfx/gl/shadowMapper.h"
-#include "gfx/gl/vertexArrayObject.h"
+#include "gfx/gl/shadowStenciller.h"
+#include "util.h"
+#include <location.h>
+
+static_assert(std::is_constructible_v<Foliage>);
+constexpr float OBJECT_BILLBOARD_DIVISOR = 64;
+constexpr float BILLBOARD_ANGLE_TOLERANCE = 250.F; // Radians per mm size
+constexpr float ASSUMED_VIEWPORT = 1440;
+constexpr float OVER_SAMPLE_MULTIPLIER = 2; // Use mesh until billboard 1/2 of rendered size
+
+namespace {
+ GLsizei
+ billboardTextureSizeForObject(RelativeDistance objectSize)
+ {
+ return static_cast<GLsizei>(std::pow(2, std::ceil(std::log2(objectSize / OBJECT_BILLBOARD_DIVISOR))));
+ }
+}
+
+std::weak_ptr<glVertexArray> Foliage::commonInstanceVAO, Foliage::commonInstancePointVAO;
+
+std::any
+Foliage::createAt(const Location & position) const
+{
+ return std::make_shared<InstanceVertices<InstanceVertex>::InstanceProxy>(
+ instances.acquire(locationData->acquire(position)));
+}
bool
Foliage::persist(Persistence::PersistenceStore & store)
@@ -13,43 +40,117 @@ void
Foliage::postLoad()
{
texture = getTexture();
- bodyMesh->configureVAO(instanceVAO)
- .addAttribs<LocationVertex, &LocationVertex::rotation, &LocationVertex::position>(
- instances.bufferName(), 1);
- VertexArrayObject {instancePointVAO}.addAttribs<LocationVertex, &LocationVertex::position, &LocationVertex::yaw>(
- instances.bufferName());
+ glDebugScope _ {0};
+ if (createIfRequired(instanceVAO, commonInstanceVAO)) {
+ bodyMesh->configureVAO(*instanceVAO, 0).addAttribs<InstanceVertex, &InstanceVertex::location>(1);
+ }
+ if (createIfRequired(instancePointVAO, commonInstancePointVAO)) {
+ instancePointVAO->configure().addAttribs<InstanceVertex, &InstanceVertex::location>(0);
+ }
+ const auto & size = bodyMesh->getDimensions().size;
+ billboardSize = billboardTextureSizeForObject(size);
+ ShadowStenciller::configureStencilTexture(shadowStencil, {billboardSize, billboardSize});
+ BillboardPainter::configureBillBoardTextures(billboard, {billboardSize, billboardSize});
+ useMeshClipDist = (ASSUMED_VIEWPORT * OVER_SAMPLE_MULTIPLIER * size) / static_cast<RelativeDistance>(billboardSize);
}
void
-Foliage::updateStencil(const ShadowStenciller & ss) const
+Foliage::updateStencil(const ShadowStenciller & shadowStenciller) const
+{
+ if (instancePartitions.second.second != instancePartitions.second.first
+ && glm::distance(shadowStenciller.getLightDirection(), shadowStencilDir)
+ > BILLBOARD_ANGLE_TOLERANCE / bodyMesh->getDimensions().size) {
+ shadowStenciller.renderStencil(shadowStencil, *bodyMesh, texture);
+ }
+}
+
+void
+Foliage::updateBillboard(const BillboardPainter & bbp) const
+{
+ if (instancePartitions.first != instancePartitions.second.first
+ && std::abs(bbp.getAngle() - billboardAngle) > BILLBOARD_ANGLE_TOLERANCE / bodyMesh->getDimensions().size) {
+ bbp.renderBillBoard(billboard, *bodyMesh, texture);
+ billboardAngle = bbp.getAngle();
+ }
+}
+
+void
+Foliage::preFrame(const Frustum & frustum, const Frustum & lighting)
{
if (instances.size() > 0) {
- ss.renderStencil(shadowStencil, *bodyMesh, texture);
+ const auto & dims = bodyMesh->getDimensions();
+ instancePartitions = instances.partition(
+ [&frustum, &dims](const auto & instance) {
+ return frustum.contains(instance.location->position.xyz() + dims.centre, dims.size);
+ },
+ [&frustum, this](const auto & instance) {
+ return distance(frustum.getPosition(), instance.location->position.xyz()) < useMeshClipDist;
+ },
+ [&lighting, &dims](const auto & instance) {
+ return lighting.contains(instance.location->position.xyz() + dims.centre, dims.size);
+ });
+ // In view frustum / Outside view frustum /
+ // Close to view / Far from view / Casts shadow into view / No shadow in view /
}
}
void
Foliage::render(const SceneShader & shader, const Frustum &) const
{
- if (const auto count = instances.size()) {
- shader.basicInst.use();
- if (texture) {
- texture->bind();
+ if (instancePartitions.first) {
+ glDebugScope _ {*instanceVAO};
+ std::ignore = instances.size();
+ if (const auto count = instancePartitions.first - instancePartitions.second.first) {
+ glDebugScope _ {0, "Billboard"};
+ const auto dimensions = bodyMesh->getDimensions();
+ shader.billboard.use(dimensions.size, dimensions.centre);
+ billboard[0].bind(0);
+ billboard[1].bind(1);
+ billboard[2].bind(2);
+ glBindVertexArray(*instancePointVAO);
+ instancePointVAO->useBuffer(0, instances);
+ glDrawArrays(GL_POINTS, static_cast<GLint>(instancePartitions.second.first), static_cast<GLsizei>(count));
+ glBindVertexArray(0);
+ }
+ if (const auto count = instancePartitions.second.first) {
+ glDebugScope _ {0, "Mesh"};
+ shader.basicInst.use();
+ if (texture) {
+ texture->bind(0);
+ }
+ instanceVAO->useBuffer(1, instances);
+ bodyMesh->drawInstanced(*instanceVAO, static_cast<GLsizei>(count));
}
- bodyMesh->DrawInstanced(instanceVAO, static_cast<GLsizei>(count));
}
}
void
Foliage::shadows(const ShadowMapper & mapper, const Frustum &) const
{
- if (const auto count = instances.size()) {
- const auto dimensions = bodyMesh->getDimensions();
- mapper.stencilShadowProgram.use(dimensions.centre, dimensions.size);
- glActiveTexture(GL_TEXTURE0);
- glBindTexture(GL_TEXTURE_2D_ARRAY, shadowStencil);
- glBindVertexArray(instancePointVAO);
- glDrawArrays(GL_POINTS, 0, static_cast<GLsizei>(count));
- glBindVertexArray(0);
+ if (instancePartitions.second.second) {
+ glDebugScope _ {*instanceVAO};
+ std::ignore = instances.size();
+ if (const auto count = instancePartitions.second.second - instancePartitions.second.first) {
+ glDebugScope _ {0, "Billboard"};
+ const auto dimensions = bodyMesh->getDimensions();
+ mapper.stencilShadowProgram.use(dimensions.centre, dimensions.size);
+ shadowStencil.bind(0);
+ glBindVertexArray(*instancePointVAO);
+ instancePointVAO->useBuffer(0, instances);
+ glDrawArrays(GL_POINTS, static_cast<GLint>(instancePartitions.second.first), static_cast<GLsizei>(count));
+ glBindVertexArray(0);
+ }
+ if (const auto count = instancePartitions.second.first) {
+ glDebugScope _ {0, "Mesh"};
+ if (texture) {
+ texture->bind(3);
+ mapper.dynamicPointInstWithTextures.use();
+ }
+ else {
+ mapper.dynamicPointInst.use();
+ }
+ instanceVAO->useBuffer(1, instances);
+ bodyMesh->drawInstanced(*instanceVAO, static_cast<GLsizei>(count));
+ }
}
}
diff --git a/game/scenary/foliage.h b/game/scenary/foliage.h
index 71bc734..f424ffc 100644
--- a/game/scenary/foliage.h
+++ b/game/scenary/foliage.h
@@ -2,7 +2,6 @@
#include "assetFactory/asset.h"
#include "gfx/gl/instanceVertices.h"
-#include "gfx/gl/shadowStenciller.h"
#include "gfx/models/texture.h"
#include "gfx/renderable.h"
@@ -13,24 +12,36 @@ class Location;
class Foliage : public Asset, public Renderable, public StdTypeDefs<Foliage> {
Mesh::Ptr bodyMesh;
Texture::Ptr texture;
- glVertexArray instanceVAO;
- glVertexArray instancePointVAO;
+ std::shared_ptr<glVertexArray> instanceVAO, instancePointVAO;
+ static std::weak_ptr<glVertexArray> commonInstanceVAO, commonInstancePointVAO;
public:
- struct LocationVertex {
- glm::mat3 rotation;
- float yaw;
- GlobalPosition3D position;
+ [[nodiscard]] std::any createAt(const Location &) const override;
+
+ struct InstanceVertex {
+ CommonLocationInstance location;
+ // float scale;
+ // something colorBias;
};
- mutable InstanceVertices<LocationVertex> instances;
+ mutable InstanceVertices<InstanceVertex> instances;
+ void preFrame(const Frustum &, const Frustum &) override;
void render(const SceneShader &, const Frustum &) const override;
void shadows(const ShadowMapper &, const Frustum &) const override;
void updateStencil(const ShadowStenciller &) const override;
- glTexture shadowStencil = ShadowStenciller::createStencilTexture(256, 256);
+ void updateBillboard(const BillboardPainter &) const override;
protected:
friend Persistence::SelectionPtrBase<std::shared_ptr<Foliage>>;
bool persist(Persistence::PersistenceStore & store) override;
void postLoad() override;
+ GLsizei billboardSize {};
+ RelativeDistance useMeshClipDist {};
+ mutable Direction2D shadowStencilDir {std::numeric_limits<Direction2D::value_type>::infinity()};
+ glTexture<GL_TEXTURE_2D_ARRAY> shadowStencil;
+ mutable Angle billboardAngle = std::numeric_limits<Angle>::infinity();
+ glTextures<GL_TEXTURE_2D_ARRAY, 3> billboard;
+
+private:
+ InstanceVertices<InstanceVertex>::PartitionResult instancePartitions;
};
diff --git a/game/scenary/illuminator.cpp b/game/scenary/illuminator.cpp
index f1a02b2..7f0c7c2 100644
--- a/game/scenary/illuminator.cpp
+++ b/game/scenary/illuminator.cpp
@@ -1,19 +1,18 @@
#include "illuminator.h"
#include "gfx/gl/sceneShader.h"
-#include "gfx/gl/vertexArrayObject.h"
#include "gfx/models/texture.h" // IWYU pragma: keep
+#include "util.h"
+#include <location.h>
-bool
-Illuminator::SpotLight::persist(Persistence::PersistenceStore & store)
-{
- return STORE_TYPE && STORE_MEMBER(position) && STORE_MEMBER(direction) && STORE_MEMBER(colour) && STORE_MEMBER(kq)
- && STORE_MEMBER(arc);
-}
+static_assert(std::is_constructible_v<Illuminator>);
-bool
-Illuminator::PointLight::persist(Persistence::PersistenceStore & store)
+std::weak_ptr<glVertexArray> Illuminator::commonInstanceVAO;
+
+std::any
+Illuminator::createAt(const Location & position) const
{
- return STORE_TYPE && STORE_MEMBER(position) && STORE_MEMBER(colour) && STORE_MEMBER(kq);
+ return std::make_shared<InstanceVertices<InstanceVertex>::InstanceProxy>(
+ instances.acquire(locationData->acquire(position)));
}
bool
@@ -31,30 +30,9 @@ Illuminator::postLoad()
throw std::logic_error {"Illuminator has no lights"};
}
texture = getTexture();
- bodyMesh->configureVAO(instanceVAO)
- .addAttribs<LocationVertex, &LocationVertex::first, &LocationVertex::second>(instances.bufferName(), 1);
- if (!spotLight.empty()) {
- instancesSpotLightVAO.emplace();
- VertexArrayObject {*instancesSpotLightVAO}
- .addAttribs<SpotLightVertex, &SpotLightVertex::position, &SpotLightVertex::direction,
- &SpotLightVertex::colour, &SpotLightVertex::kq, &SpotLightVertex::arc>(
- instancesSpotLight.bufferName(), 0)
- .addAttribs<LocationVertex, &LocationVertex::first, &LocationVertex::second>(instances.bufferName(), 1);
- std::transform(
- spotLight.begin(), spotLight.end(), std::back_inserter(spotLightInstances), [this](const auto & s) {
- return instancesSpotLight.acquire(*s);
- });
- }
- if (!pointLight.empty()) {
- instancesPointLightVAO.emplace();
- VertexArrayObject {*instancesPointLightVAO}
- .addAttribs<PointLightVertex, &PointLightVertex::position, &PointLightVertex::colour,
- &PointLightVertex::kq>(instancesPointLight.bufferName(), 0)
- .addAttribs<LocationVertex, &LocationVertex::first, &LocationVertex::second>(instances.bufferName(), 1);
- std::transform(
- pointLight.begin(), pointLight.end(), std::back_inserter(pointLightInstances), [this](const auto & s) {
- return instancesPointLight.acquire(*s);
- });
+ glDebugScope _ {0};
+ if (createIfRequired(instanceVAO, commonInstanceVAO)) {
+ bodyMesh->configureVAO(*instanceVAO, 0).addAttribs<InstanceVertex, &InstanceVertex::location>(1);
}
}
@@ -62,29 +40,12 @@ void
Illuminator::render(const SceneShader & shader, const Frustum &) const
{
if (const auto count = instances.size()) {
+ glDebugScope _ {*instanceVAO};
shader.basicInst.use();
if (texture) {
- texture->bind();
+ texture->bind(0);
}
- bodyMesh->DrawInstanced(instanceVAO, static_cast<GLsizei>(count));
- }
-}
-
-void
-Illuminator::lights(const SceneShader & shader) const
-{
- if (const auto count = instances.size()) {
- if (const auto scount = instancesSpotLight.size()) {
- shader.spotLightInst.use();
- glBindVertexArray(*instancesSpotLightVAO);
- glDrawArraysInstanced(GL_POINTS, 0, static_cast<GLsizei>(scount), static_cast<GLsizei>(count));
- }
- if (const auto pcount = instancesPointLight.size()) {
- shader.pointLightInst.use();
- glBindVertexArray(*instancesPointLightVAO);
- glDrawArraysInstanced(GL_POINTS, 0, static_cast<GLsizei>(pcount), static_cast<GLsizei>(count));
- }
-
- glBindVertexArray(0);
+ instanceVAO->useBuffer(1, instances);
+ bodyMesh->drawInstanced(*instanceVAO, static_cast<GLsizei>(count));
}
}
diff --git a/game/scenary/illuminator.h b/game/scenary/illuminator.h
index 47ce337..2373812 100644
--- a/game/scenary/illuminator.h
+++ b/game/scenary/illuminator.h
@@ -1,6 +1,7 @@
#pragma once
#include "assetFactory/asset.h"
+#include "game/mixins/lights.h"
#include "gfx/gl/instanceVertices.h"
#include "gfx/models/texture.h"
#include "gfx/renderable.h"
@@ -8,53 +9,24 @@
class SceneShader;
class Location;
-class Illuminator : public Asset, public Renderable, public StdTypeDefs<Illuminator> {
+class Illuminator : public Asset, public Renderable, public AssetLights, public StdTypeDefs<Illuminator> {
Mesh::Ptr bodyMesh;
Texture::Ptr texture;
- glVertexArray instanceVAO;
- std::optional<glVertexArray> instancesSpotLightVAO, instancesPointLightVAO;
+ std::shared_ptr<glVertexArray> instanceVAO;
+ static std::weak_ptr<glVertexArray> commonInstanceVAO;
public:
- struct LightCommonVertex {
- RelativePosition3D position;
- RGB colour;
- RelativeDistance kq;
- };
-
- struct SpotLightVertex : LightCommonVertex {
- Direction3D direction;
- Angle arc;
- };
-
- struct PointLightVertex : LightCommonVertex { };
+ [[nodiscard]] std::any createAt(const Location &) const override;
- struct SpotLight : Persistence::Persistable, SpotLightVertex, StdTypeDefs<SpotLight> {
- private:
- friend Persistence::SelectionPtrBase<std::shared_ptr<SpotLight>>;
- bool persist(Persistence::PersistenceStore & store) override;
+ struct InstanceVertex {
+ CommonLocationInstance location;
};
- struct PointLight : Persistence::Persistable, PointLightVertex, StdTypeDefs<PointLight> {
- private:
- friend Persistence::SelectionPtrBase<std::shared_ptr<PointLight>>;
- bool persist(Persistence::PersistenceStore & store) override;
- };
-
-public:
- using LocationVertex = std::pair<glm::mat3, GlobalPosition3D>;
- mutable InstanceVertices<LocationVertex> instances;
- mutable InstanceVertices<SpotLightVertex> instancesSpotLight;
- mutable InstanceVertices<PointLightVertex> instancesPointLight;
+ mutable InstanceVertices<InstanceVertex> instances;
void render(const SceneShader &, const Frustum &) const override;
- void lights(const SceneShader &) const override;
protected:
friend Persistence::SelectionPtrBase<std::shared_ptr<Illuminator>>;
bool persist(Persistence::PersistenceStore & store) override;
void postLoad() override;
-
- std::vector<SpotLight::Ptr> spotLight;
- std::vector<PointLight::Ptr> pointLight;
- std::vector<InstanceVertices<SpotLightVertex>::InstanceProxy> spotLightInstances;
- std::vector<InstanceVertices<PointLightVertex>::InstanceProxy> pointLightInstances;
};
diff --git a/game/scenary/light.cpp b/game/scenary/light.cpp
index 6207497..455d5b5 100644
--- a/game/scenary/light.cpp
+++ b/game/scenary/light.cpp
@@ -2,6 +2,8 @@
#include "location.h"
Light::Light(std::shared_ptr<const Illuminator> type, const Location & position) :
- type {std::move(type)}, location {this->type->instances.acquire(position.getRotationTransform(), position.pos)}
+ type {std::move(type)},
+ instance {this->type->instances.acquire(Renderable::commonLocationData.lock()->acquire(position))}
{
+ lightsEnable(this->type, instance->location.index);
}
diff --git a/game/scenary/light.h b/game/scenary/light.h
index 0b19535..0b9320c 100644
--- a/game/scenary/light.h
+++ b/game/scenary/light.h
@@ -5,15 +5,18 @@
class Location;
-class Light : public WorldObject {
+class Light : public WorldObject, public InstanceLights {
std::shared_ptr<const Illuminator> type;
- InstanceVertices<Illuminator::LocationVertex>::InstanceProxy location;
+ InstanceVertices<Illuminator::InstanceVertex>::InstanceProxy instance;
void
tick(TickDuration) override
{
}
+ std::vector<InstanceVertices<SpotLightVertex>::InstanceProxy> spotLightInstances;
+ std::vector<InstanceVertices<PointLightVertex>::InstanceProxy> pointLightInstances;
+
public:
Light(std::shared_ptr<const Illuminator> type, const Location & position);
};
diff --git a/game/scenary/plant.cpp b/game/scenary/plant.cpp
index 2006225..b0e7d16 100644
--- a/game/scenary/plant.cpp
+++ b/game/scenary/plant.cpp
@@ -3,6 +3,6 @@
Plant::Plant(std::shared_ptr<const Foliage> type, const Location & position) :
type {std::move(type)},
- location {this->type->instances.acquire(position.getRotationTransform(), position.rot.y, position.pos)}
+ instance {this->type->instances.acquire(Renderable::commonLocationData.lock()->acquire(position))}
{
}
diff --git a/game/scenary/plant.h b/game/scenary/plant.h
index 77c9ff7..cc690c5 100644
--- a/game/scenary/plant.h
+++ b/game/scenary/plant.h
@@ -7,7 +7,7 @@ class Location;
class Plant : public WorldObject {
std::shared_ptr<const Foliage> type;
- InstanceVertices<Foliage::LocationVertex>::InstanceProxy location;
+ InstanceVertices<Foliage::InstanceVertex>::InstanceProxy instance;
void
tick(TickDuration) override
diff --git a/game/terrain.cpp b/game/terrain.cpp
index f10aac6..f3d7a7d 100644
--- a/game/terrain.cpp
+++ b/game/terrain.cpp
@@ -6,7 +6,7 @@
#include <gfx/image.h>
#include <gfx/models/mesh.h>
#include <gfx/models/vertex.h>
-#include <glMappedBufferWriter.h>
+#include <glMappedBufferSpan.h>
#include <glm/glm.hpp>
#include <location.h>
#include <maths.h>
@@ -16,11 +16,13 @@
static constexpr RGB OPEN_SURFACE {-1};
static constexpr GlobalDistance TILE_SIZE = 1024 * 1024; // ~1km, power of 2, fast divide
-template<>
-VertexArrayObject &
-VertexArrayObject::addAttribsFor<Terrain::Vertex>(const GLuint arrayBuffer, const GLuint divisor)
+void
+Terrain::initialise()
{
- return addAttribs<Terrain::Vertex, &Terrain::Vertex::pos, &Terrain::Vertex::normal>(arrayBuffer, divisor);
+ glDebugScope _ {0};
+ vertexArray.configure().addAttribs<Terrain::Vertex, &Terrain::Vertex::pos, &Terrain::Vertex::normal>(
+ 0, verticesBuffer);
+ generateMeshes();
}
bool
@@ -33,7 +35,8 @@ Terrain::SurfaceKey::operator<(const SurfaceKey & other) const
inline void
Terrain::copyVerticesToBuffer() const
{
- std::ranges::transform(all_vertices(), glMappedBufferWriter<Vertex> {GL_ARRAY_BUFFER, verticesBuffer, n_vertices()},
+ std::ranges::transform(all_vertices(),
+ glMappedBufferSpan<Vertex> {verticesBuffer, n_vertices(), GL_WRITE_ONLY, true}.begin(),
[this](const auto & vertex) {
return Vertex {point(vertex), normal(vertex)};
});
@@ -75,21 +78,10 @@ void
Terrain::copyIndicesToBuffers(const SurfaceIndices & surfaceIndices)
{
for (const auto & [surfaceKey, indices] : surfaceIndices) {
- auto meshItr = meshes.find(surfaceKey);
- if (meshItr == meshes.end()) {
- meshItr = meshes.emplace(surfaceKey, SurfaceArrayBuffer {}).first;
- VertexArrayObject {meshItr->second.vertexArray}
- .addAttribsFor<Vertex>(verticesBuffer)
- .addIndices(meshItr->second.indicesBuffer, indices)
- .data(verticesBuffer, GL_ARRAY_BUFFER);
- }
- else {
- VertexArrayObject {meshItr->second.vertexArray}
- .addIndices(meshItr->second.indicesBuffer, indices)
- .data(verticesBuffer, GL_ARRAY_BUFFER);
- }
- meshItr->second.count = static_cast<GLsizei>(indices.size());
- meshItr->second.aabb = AxisAlignedBoundingBox<GlobalDistance>::fromPoints(
+ auto & mesh = meshes[surfaceKey];
+ mesh.indicesBuffer.data(indices, GL_DYNAMIC_DRAW);
+ mesh.count = static_cast<GLsizei>(indices.size());
+ mesh.aabb = AxisAlignedBoundingBox<GlobalDistance>::fromPoints(
indices | std::views::transform([this](const auto vertex) {
return this->point(VertexHandle {static_cast<int>(vertex)});
}));
@@ -109,6 +101,7 @@ Terrain::pruneOrphanMeshes(const SurfaceIndices & surfaceIndices)
void
Terrain::generateMeshes()
{
+ glDebugScope _ {0};
copyVerticesToBuffer();
const auto surfaceIndices = mapSurfaceFacesToIndices();
copyIndicesToBuffers(surfaceIndices);
@@ -129,7 +122,9 @@ Terrain::afterChange()
void
Terrain::render(const SceneShader & shader, const Frustum & frustum) const
{
- grass->bind();
+ glDebugScope _ {0};
+ glBindVertexArray(vertexArray);
+ grass->bind(0);
const auto chunkBySurface = std::views::chunk_by([](const auto & itr1, const auto & itr2) {
return itr1.first.surface == itr2.first.surface;
@@ -139,7 +134,7 @@ Terrain::render(const SceneShader & shader, const Frustum & frustum) const
shader.landmass.use(surface ? surface->colorBias : OPEN_SURFACE);
for (const auto & sab : surfaceRange) {
if (frustum.contains(sab.second.aabb)) {
- glBindVertexArray(sab.second.vertexArray);
+ glVertexArrayElementBuffer(vertexArray, sab.second.indicesBuffer);
glDrawElements(GL_TRIANGLES, sab.second.count, GL_UNSIGNED_INT, nullptr);
}
}
@@ -150,10 +145,12 @@ Terrain::render(const SceneShader & shader, const Frustum & frustum) const
void
Terrain::shadows(const ShadowMapper & shadowMapper, const Frustum & frustum) const
{
+ glDebugScope _ {0};
+ glBindVertexArray(vertexArray);
shadowMapper.landmess.use();
for (const auto & [surface, sab] : meshes) {
- if (frustum.shadedBy(sab.aabb)) {
- glBindVertexArray(sab.vertexArray);
+ if (frustum.contains(sab.aabb)) {
+ glVertexArrayElementBuffer(vertexArray, sab.indicesBuffer);
glDrawElements(GL_TRIANGLES, sab.count, GL_UNSIGNED_INT, nullptr);
}
}
diff --git a/game/terrain.h b/game/terrain.h
index 1a63296..f1170f2 100644
--- a/game/terrain.h
+++ b/game/terrain.h
@@ -13,7 +13,7 @@ class Terrain : public GeoData, public WorldObject, public Renderable {
public:
template<typename... P> explicit Terrain(P &&... params) : GeoData {std::forward<P>(params)...}
{
- generateMeshes();
+ initialise();
}
void render(const SceneShader & shader, const Frustum &) const override;
@@ -29,15 +29,17 @@ public:
void generateMeshes();
private:
+ void initialise();
void afterChange() override;
struct SurfaceArrayBuffer {
- glVertexArray vertexArray;
glBuffer indicesBuffer;
GLsizei count;
AxisAlignedBoundingBox<GlobalDistance> aabb;
};
+ glVertexArray vertexArray;
+
struct SurfaceKey {
const Surface * surface;
GlobalPosition2D basePosition;
diff --git a/game/vehicles/railVehicle.cpp b/game/vehicles/railVehicle.cpp
index 59d1e83..b5de833 100644
--- a/game/vehicles/railVehicle.cpp
+++ b/game/vehicles/railVehicle.cpp
@@ -11,48 +11,46 @@
#include <maths.h>
#include <ray.h>
-RailVehicle::RailVehicle(RailVehicleClassPtr rvc) :
- RailVehicleClass::Instance {rvc->instances.acquire()}, rvClass {std::move(rvc)},
- location {[this](const BufferedLocation * l) {
- this->get()->body = l->getRotationTransform();
- this->get()->bodyPos = l->position();
- }},
- bogies {{
- {[this](const BufferedLocation * l) {
- this->get()->front = l->getRotationTransform();
- this->get()->frontPos = l->position();
- },
- GlobalPosition3D {0, rvClass->wheelBase / 2.F, 0}},
- {[this](const BufferedLocation * l) {
- this->get()->back = l->getRotationTransform();
- this->get()->backPos = l->position();
- },
- GlobalPosition3D {0, -rvClass->wheelBase / 2.F, 0}},
- }}
+RailVehicle::RailVehicle(RailVehicleClassPtr rvc, GlobalPosition3D position) :
+ RailVehicleClass::Instance {rvc->instances.acquire(
+ RailVehicleClass::commonLocationData.lock()->acquire(Location {.pos = position, .rot = {}}),
+ RailVehicleClass::commonLocationData.lock()->acquire(
+ Location {.pos = position + RelativePosition3D {0, rvc->wheelBase / 2.F, 0}, .rot = {}}),
+ RailVehicleClass::commonLocationData.lock()->acquire(
+ Location {.pos = position + RelativePosition3D {0, -rvc->wheelBase / 2.F, 0}, .rot = {}}))},
+ rvClass {std::move(rvc)}
{
+ lightsEnable(rvClass, get()->body.index);
}
void
RailVehicle::move(const Train * t, float & trailBy)
{
const auto overhang {(rvClass->length - rvClass->wheelBase) / 2};
- const auto & b1Pos = bogies[0] = t->getBogiePosition(t->linkDist, trailBy += overhang);
- const auto & b2Pos = bogies[1] = t->getBogiePosition(t->linkDist, trailBy += rvClass->wheelBase);
- const auto diff = glm::normalize(RelativePosition3D(b2Pos.position() - b1Pos.position()));
- location.setLocation((b1Pos.position() + b2Pos.position()) / 2, {vector_pitch(diff), vector_yaw(diff), 0});
+ const auto & b1Pos = *(get()->front = t->getBogiePosition(t->linkDist, trailBy += overhang));
+ const auto & b2Pos = *(get()->back = t->getBogiePosition(t->linkDist, trailBy += rvClass->wheelBase));
+ const auto diff = glm::normalize(difference(b1Pos.position, b2Pos.position));
+ get()->body = Location {
+ .pos = midpoint(b1Pos.position, b2Pos.position), .rot = {vector_pitch(diff), vector_yaw(diff), 0}};
trailBy += 600.F + overhang;
}
+Location
+RailVehicle::getLocation() const
+{
+ return {.pos = get()->body->position, .rot = get()->body->rotation};
+}
+
bool
RailVehicle::intersectRay(const Ray<GlobalPosition3D> & ray, BaryPosition & baryPos, RelativeDistance & distance) const
{
constexpr const auto X = 1350.F;
const auto Y = this->rvClass->length / 2.F;
constexpr const auto Z = 3900.F;
- const glm::mat3 moveBy = location.getRotationTransform();
- const auto cornerVertices = cuboidCorners(-X, X, -Y, Y, 0.F, Z) * [&moveBy, this](const auto & corner) {
- return location.position() + (moveBy * corner);
- };
+ const auto cornerVertices
+ = cuboidCorners(-X, X, -Y, Y, 0.F, Z) * [body = this->get()->body.get()](const auto & corner) {
+ return body->position + (body->rotationMatrix * corner);
+ };
static constexpr const std::array<glm::vec<3, uint8_t>, 10> triangles {{
// Front
{0, 1, 2},
@@ -72,7 +70,12 @@ RailVehicle::intersectRay(const Ray<GlobalPosition3D> & ray, BaryPosition & bary
}};
return std::any_of(
triangles.begin(), triangles.end(), [&cornerVertices, &ray, &baryPos, &distance](const auto & idx) {
- return ray.intersectTriangle(
- cornerVertices[idx[0]], cornerVertices[idx[1]], cornerVertices[idx[2]], baryPos, distance);
+ if (const auto inter = ray.intersectTriangle(
+ cornerVertices[idx[0]], cornerVertices[idx[1]], cornerVertices[idx[2]])) {
+ baryPos = inter->bary;
+ distance = inter->distance;
+ return true;
+ };
+ return false;
});
}
diff --git a/game/vehicles/railVehicle.h b/game/vehicles/railVehicle.h
index bf1e782..c02c19e 100644
--- a/game/vehicles/railVehicle.h
+++ b/game/vehicles/railVehicle.h
@@ -1,8 +1,6 @@
#pragma once
-#include "gfx/gl/bufferedLocation.h"
#include "railVehicleClass.h"
-#include <array>
#include <game/selectable.h>
#include <glm/glm.hpp>
#include <memory>
@@ -10,18 +8,16 @@
template<typename> class Ray;
class Train;
-class RailVehicle : Selectable, RailVehicleClass::Instance {
+class RailVehicle : Selectable, RailVehicleClass::Instance, public InstanceLights {
public:
- explicit RailVehicle(RailVehicleClassPtr rvc);
+ explicit RailVehicle(RailVehicleClassPtr rvc, GlobalPosition3D = {});
void move(const Train *, float & trailBy);
+ [[nodiscard]] Location getLocation() const;
[[nodiscard]] bool intersectRay(const Ray<GlobalPosition3D> &, BaryPosition &, RelativeDistance &) const override;
RailVehicleClassPtr rvClass;
- using LV = RailVehicleClass::LocationVertex;
- BufferedLocationUpdater location;
- std::array<BufferedLocationUpdater, 2> bogies;
};
using RailVehiclePtr = std::unique_ptr<RailVehicle>;
diff --git a/game/vehicles/railVehicleClass.cpp b/game/vehicles/railVehicleClass.cpp
index 162a29a..cfdc52d 100644
--- a/game/vehicles/railVehicleClass.cpp
+++ b/game/vehicles/railVehicleClass.cpp
@@ -1,7 +1,6 @@
#include "railVehicleClass.h"
#include "gfx/gl/sceneShader.h"
#include "gfx/gl/shadowMapper.h"
-#include "gfx/gl/vertexArrayObject.h"
#include <array>
#include <glm/glm.hpp>
#include <lib/resource.h>
@@ -13,46 +12,62 @@ bool
RailVehicleClass::persist(Persistence::PersistenceStore & store)
{
return STORE_TYPE && STORE_MEMBER(length) && STORE_MEMBER(wheelBase) && STORE_MEMBER(maxSpeed)
- && STORE_NAME_HELPER("bogie", bogies, Asset::MeshArrayConstruct)
+ && STORE_NAME_HELPER("bogie", bogies, Asset::MeshArrayConstruct) && AssetLights::persist(store)
&& STORE_HELPER(bodyMesh, Asset::MeshConstruct) && Asset::persist(store);
}
+std::any
+RailVehicleClass::createAt(const Location & position) const
+{
+ return std::make_shared<InstanceVertices<InstanceVertex>::InstanceProxy>(instances.acquire(InstanceVertex {
+ .body = locationData->acquire(position),
+ .front = locationData->acquire(position + ((sincos(position.rot.x) * wheelBase * 0.5F) || 0.F)),
+ .back = locationData->acquire(position + ((sincos(position.rot.x) * wheelBase * -0.5F) || 0.F)),
+ }));
+}
+
void
RailVehicleClass::postLoad()
{
texture = getTexture();
- bodyMesh->configureVAO(instanceVAO)
- .addAttribs<LocationVertex, &LocationVertex::body, &LocationVertex::bodyPos>(instances.bufferName(), 1);
- bogies.front()
- ->configureVAO(instancesBogiesVAO.front())
- .addAttribs<LocationVertex, &LocationVertex::front, &LocationVertex::frontPos>(instances.bufferName(), 1);
- bogies.back()
- ->configureVAO(instancesBogiesVAO.back())
- .addAttribs<LocationVertex, &LocationVertex::back, &LocationVertex::backPos>(instances.bufferName(), 1);
- static_assert(sizeof(LocationVertex) == 144UL);
+ glDebugScope _ {0};
+ bodyMesh->configureVAO(instanceVAO, 0).addAttribs<InstanceVertex, &InstanceVertex::body>(1);
+}
+
+void
+RailVehicleClass::renderAllParts(const size_t count) const
+{
+ using PartPair = std::pair<Mesh::Ptr, CommonLocationInstance InstanceVertex::*>;
+ const auto bufferName = instances.bufferName();
+ for (const auto & [mesh, part] : {
+ PartPair {bodyMesh, &InstanceVertex::body},
+ PartPair {bogies.front(), &InstanceVertex::front},
+ PartPair {bogies.back(), &InstanceVertex::back},
+ }) {
+ instanceVAO.useBuffer<InstanceVertex>(1, bufferName, part);
+ mesh->drawInstanced(instanceVAO, static_cast<GLsizei>(count));
+ }
}
void
RailVehicleClass::render(const SceneShader & shader, const Frustum &) const
{
- if (const auto count = static_cast<GLsizei>(instances.size())) {
+ if (const auto count = (instances.size())) {
+ glDebugScope _ {instanceVAO};
if (texture) {
- texture->bind();
+ texture->bind(0);
}
shader.basicInst.use();
- bodyMesh->DrawInstanced(instanceVAO, count);
- bogies.front()->DrawInstanced(instancesBogiesVAO.front(), count);
- bogies.back()->DrawInstanced(instancesBogiesVAO.back(), count);
+ renderAllParts(count);
}
}
void
RailVehicleClass::shadows(const ShadowMapper & mapper, const Frustum &) const
{
- if (const auto count = static_cast<GLsizei>(instances.size())) {
+ if (const auto count = instances.size()) {
+ glDebugScope _ {instanceVAO};
mapper.dynamicPointInst.use();
- bodyMesh->DrawInstanced(instanceVAO, count);
- bogies.front()->DrawInstanced(instancesBogiesVAO.front(), count);
- bogies.back()->DrawInstanced(instancesBogiesVAO.back(), count);
+ renderAllParts(count);
}
}
diff --git a/game/vehicles/railVehicleClass.h b/game/vehicles/railVehicleClass.h
index 6eb4ca5..1ea87cd 100644
--- a/game/vehicles/railVehicleClass.h
+++ b/game/vehicles/railVehicleClass.h
@@ -1,6 +1,7 @@
#pragma once
#include "assetFactory/asset.h"
+#include "game/mixins/lights.h"
#include "gfx/gl/instanceVertices.h"
#include "gfx/models/mesh.h"
#include "gfx/models/texture.h"
@@ -12,14 +13,15 @@ class SceneShader;
class ShadowMapper;
class Location;
-class RailVehicleClass : public Renderable, public Asset {
+class RailVehicleClass : public Renderable, public Asset, public AssetLights {
public:
void render(const SceneShader & shader, const Frustum &) const override;
void shadows(const ShadowMapper & shadowMapper, const Frustum &) const override;
- struct LocationVertex {
- glm::mat3 body, front, back;
- GlobalPosition3D bodyPos, frontPos, backPos;
+ [[nodiscard]] std::any createAt(const Location &) const override;
+
+ struct InstanceVertex {
+ CommonLocationInstance body, front, back;
};
std::array<Mesh::Ptr, 2> bogies;
@@ -29,17 +31,17 @@ public:
float length;
float maxSpeed;
- mutable InstanceVertices<LocationVertex> instances;
+ mutable InstanceVertices<InstanceVertex> instances;
using Instance = decltype(instances)::InstanceProxy;
protected:
friend Persistence::SelectionPtrBase<std::shared_ptr<RailVehicleClass>>;
bool persist(Persistence::PersistenceStore & store) override;
void postLoad() override;
+ void renderAllParts(size_t count) const;
private:
glVertexArray instanceVAO;
- std::array<glVertexArray, 2> instancesBogiesVAO;
};
using RailVehicleClassPtr = std::shared_ptr<RailVehicleClass>;
diff --git a/game/vehicles/train.cpp b/game/vehicles/train.cpp
index 2461d9c..c79fd17 100644
--- a/game/vehicles/train.cpp
+++ b/game/vehicles/train.cpp
@@ -21,6 +21,12 @@ Train::getBogiePosition(float linkDist, float dist) const
return b2Link.first->positionAt(b2linkDist, b2Link.second);
}
+Location
+Train::getLocation() const
+{
+ return objects.front()->getLocation();
+}
+
bool
Train::intersectRay(const Ray<GlobalPosition3D> & ray, BaryPosition & baryPos, RelativeDistance & distance) const
{
diff --git a/game/vehicles/train.h b/game/vehicles/train.h
index 88e30f9..9ca53a8 100644
--- a/game/vehicles/train.h
+++ b/game/vehicles/train.h
@@ -19,12 +19,7 @@ class Train : public Vehicle, public UniqueCollection<RailVehicle>, public Can<G
public:
explicit Train(const Link::CPtr & link, float linkDist = 0) : Vehicle {link, linkDist} { }
- [[nodiscard]] const Location &
- getLocation() const override
- {
- return objects.front()->location;
- }
-
+ [[nodiscard]] Location getLocation() const override;
[[nodiscard]] bool intersectRay(const Ray<GlobalPosition3D> &, BaryPosition &, RelativeDistance &) const override;
void tick(TickDuration elapsed) override;
diff --git a/game/vehicles/vehicle.h b/game/vehicles/vehicle.h
index c3b35b7..cca8ff0 100644
--- a/game/vehicles/vehicle.h
+++ b/game/vehicles/vehicle.h
@@ -18,7 +18,7 @@ public:
float linkDist; // distance along current link
float speed {}; // speed in m/s (~75 km/h)
- [[nodiscard]] virtual const Location & getLocation() const = 0;
+ [[nodiscard]] virtual Location getLocation() const = 0;
Orders orders;
ActivityPtr currentActivity;
diff --git a/game/water.cpp b/game/water.cpp
index 527e85a..96f35cf 100644
--- a/game/water.cpp
+++ b/game/water.cpp
@@ -1,5 +1,6 @@
#include "water.h"
#include "game/geoData.h"
+#include "gfx/gl/gldebug.h"
#include "gfx/models/texture.h"
#include <algorithm>
#include <cstddef>
@@ -24,10 +25,10 @@ namespace glm {
}
template<>
-VertexArrayObject &
-VertexArrayObject::addAttribsFor<Water::Vertex>(const GLuint arrayBuffer, const GLuint divisor)
+Impl::VertexArrayConfigurator &
+Impl::VertexArrayConfigurator::addAttribsFor<Water::Vertex>(const GLuint divisor)
{
- return addAttribs<Water::Vertex, &Water::Vertex::pos>(arrayBuffer, divisor);
+ return addAttribs<Water::Vertex, &Water::Vertex::pos>(divisor);
}
Water::Water(std::shared_ptr<GeoData> tm) : geoData {std::move(tm)}, water {std::make_shared<Texture>("water.png")}
@@ -42,6 +43,7 @@ static constexpr GlobalDistance BORDER = TILE_SIZE / 2;
void
Water::generateMeshes()
{
+ glDebugScope _ {0};
// Map out where a water square needs to exist to cover all terrain faces with a low vertex
std::set<GlobalPosition2D> waterPositions;
std::for_each(geoData->vertices_sbegin(), geoData->vertices_end(), [this, &waterPositions](const auto vh) {
@@ -104,7 +106,8 @@ Water::tick(TickDuration dur)
void
Water::render(const SceneShader & shader, const Frustum &) const
{
+ glDebugScope _ {0};
shader.water.use(waveCycle);
- water->bind();
- meshes.apply(&MeshT<GlobalPosition3D>::Draw);
+ water->bind(0);
+ meshes.apply(&MeshT<GlobalPosition3D>::draw);
}