diff options
Diffstat (limited to 'game')
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); } |
