From 625b78bba16dcbbe97fa84bbf8f2273472d79ade Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Thu, 6 Mar 2025 00:25:22 +0000 Subject: Pass frustum into render functions Support for culling objects outside the view frustum --- gfx/gl/sceneProvider.h | 3 ++- gfx/gl/sceneRenderer.cpp | 2 +- gfx/renderable.h | 3 ++- 3 files changed, 5 insertions(+), 3 deletions(-) (limited to 'gfx') diff --git a/gfx/gl/sceneProvider.h b/gfx/gl/sceneProvider.h index f5e8e99..93b384f 100644 --- a/gfx/gl/sceneProvider.h +++ b/gfx/gl/sceneProvider.h @@ -5,6 +5,7 @@ class SceneRenderer; class ShadowMapper; class SceneShader; +class Frustum; class SceneProvider { public: @@ -12,7 +13,7 @@ public: virtual ~SceneProvider() = default; DEFAULT_MOVE_COPY(SceneProvider); - virtual void content(const SceneShader &) const = 0; + virtual void content(const SceneShader &, const Frustum &) const = 0; virtual void environment(const SceneShader &, const SceneRenderer &) const; virtual void lights(const SceneShader &) const = 0; virtual void shadows(const ShadowMapper &) const; diff --git a/gfx/gl/sceneRenderer.cpp b/gfx/gl/sceneRenderer.cpp index b2a7d78..188c4fd 100644 --- a/gfx/gl/sceneRenderer.cpp +++ b/gfx/gl/sceneRenderer.cpp @@ -71,7 +71,7 @@ SceneRenderer::render(const SceneProvider & scene) const glEnable(GL_DEPTH_TEST); glClearColor(0, 0, 0, 1); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - scene.content(shader); + scene.content(shader, camera); // Environment pass - // * ambient - clears illumination texture - see setAmbientLight diff --git a/gfx/renderable.h b/gfx/renderable.h index 83522e3..9fbeccd 100644 --- a/gfx/renderable.h +++ b/gfx/renderable.h @@ -3,6 +3,7 @@ #include class SceneShader; +class Frustum; class ShadowMapper; class ShadowStenciller; @@ -12,7 +13,7 @@ public: virtual ~Renderable() = default; DEFAULT_MOVE_COPY(Renderable); - virtual void render(const SceneShader & shader) const = 0; + virtual void render(const SceneShader & shader, const Frustum &) const = 0; virtual void lights(const SceneShader & shader) const; virtual void shadows(const ShadowMapper & shadowMapper) const; -- cgit v1.2.3 From ba69d51cc372197ef55feb87a33ed03afd1b0ca3 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Thu, 6 Mar 2025 02:41:09 +0000 Subject: Create AxisAlignedBoundingBox Used to define the extents of GeoData mesh --- game/geoData.cpp | 21 ++++++++++----------- game/geoData.h | 7 ++++--- game/water.cpp | 2 +- gfx/aabb.cpp | 24 ++++++++++++++++++++++++ gfx/aabb.h | 17 +++++++++++++++++ test/test-lib.cpp | 12 ++++++++++++ 6 files changed, 68 insertions(+), 15 deletions(-) create mode 100644 gfx/aabb.cpp create mode 100644 gfx/aabb.h (limited to 'gfx') diff --git a/game/geoData.cpp b/game/geoData.cpp index 4291a64..5cea4dd 100644 --- a/game/geoData.cpp +++ b/game/geoData.cpp @@ -35,16 +35,16 @@ GeoData::loadFromAsciiGrid(const std::filesystem::path & input) std::vector vertices; vertices.reserve(ncols * nrows); GeoData mesh; - mesh.lowerExtent = {xllcorner, yllcorner, std::numeric_limits::max()}; - mesh.upperExtent = {xllcorner + (cellsize * (ncols - 1)), yllcorner + (cellsize * (nrows - 1)), - std::numeric_limits::min()}; + mesh.extents = {{xllcorner, yllcorner, std::numeric_limits::max()}, + {xllcorner + (cellsize * (ncols - 1)), yllcorner + (cellsize * (nrows - 1)), + std::numeric_limits::min()}}; for (size_t row = 0; row < nrows; ++row) { for (size_t col = 0; col < ncols; ++col) { float heightf = 0; f >> heightf; const auto height = static_cast(std::round(heightf * 1000.F)); - mesh.upperExtent.z = std::max(mesh.upperExtent.z, height); - mesh.lowerExtent.z = std::min(mesh.lowerExtent.z, height); + mesh.extents.max.z = std::max(mesh.extents.max.z, height); + mesh.extents.min.z = std::min(mesh.extents.min.z, height); vertices.push_back(mesh.add_vertex({xllcorner + (col * cellsize), yllcorner + (row * cellsize), height})); } } @@ -78,8 +78,7 @@ GeoData::createFlat(GlobalPosition2D lower, GlobalPosition2D upper, GlobalDistan assert((upper - lower) % GRID_SIZE == GlobalPosition2D {}); GeoData mesh; - mesh.lowerExtent = {lower, h}; - mesh.upperExtent = {upper, h}; + mesh.extents = {{lower, h}, {upper, h}}; std::vector vertices; for (GlobalDistance row = lower.x; row <= upper.x; row += GRID_SIZE) { @@ -120,7 +119,7 @@ GeoData::intersectRay(const Ray & ray, FaceHandle face) const { GeoData::IntersectionResult out; walkUntil(PointFace {ray.start, face}, - ray.start.xy() + (ray.direction.xy() * RelativePosition2D(upperExtent.xy() - lowerExtent.xy())), + ray.start.xy() + (ray.direction.xy() * ::difference(extents.max.xy(), extents.min.xy())), [&out, &ray, this](const auto & step) { BaryPosition bari {}; RelativeDistance dist {}; @@ -325,9 +324,9 @@ GeoData::setHeights(const std::span triangleStrip, const if (triangleStrip.size() < 3) { return {}; } - const auto stripMinMax = std::ranges::minmax(triangleStrip, {}, &GlobalPosition3D::z); - lowerExtent.z = std::min(upperExtent.z, stripMinMax.min.z); - upperExtent.z = std::max(upperExtent.z, stripMinMax.max.z); + for (const auto & vertex : triangleStrip) { + extents += vertex; + } class SetHeights { public: diff --git a/game/geoData.h b/game/geoData.h index b2a75bd..a504f9b 100644 --- a/game/geoData.h +++ b/game/geoData.h @@ -2,6 +2,7 @@ #include "collections.h" // IWYU pragma: keep IterableCollection #include "geoDataMesh.h" +#include "gfx/aabb.h" #include "surface.h" #include #include @@ -57,10 +58,10 @@ public: std::set setHeights(std::span triangleStrip, const SetHeightsOpts &); - [[nodiscard]] auto + [[nodiscard]] auto & getExtents() const { - return std::tie(lowerExtent, upperExtent); + return extents; } template @@ -80,5 +81,5 @@ protected: virtual void afterChange(); private: - GlobalPosition3D lowerExtent {}, upperExtent {}; + AxisAlignedBoundingBox extents; }; diff --git a/game/water.cpp b/game/water.cpp index 94a8596..527e85a 100644 --- a/game/water.cpp +++ b/game/water.cpp @@ -82,7 +82,7 @@ Water::generateMeshes() const auto pos = (p * TILE_SIZE) + GlobalPosition2D {x, y}; const auto v = vertexIndex.emplace(pos, vertices.size()); if (v.second) { - const auto cpos = glm::clamp(pos, std::get<0>(extents).xy(), std::get<1>(extents).xy()); + const auto cpos = glm::clamp(pos, extents.min.xy(), extents.max.xy()); vertices.emplace_back(geoData->positionAt(cpos)); } *out++ = static_cast(v.first->second); diff --git a/gfx/aabb.cpp b/gfx/aabb.cpp new file mode 100644 index 0000000..862dacb --- /dev/null +++ b/gfx/aabb.cpp @@ -0,0 +1,24 @@ +#include "aabb.h" +#include +#include + +AxisAlignedBoundingBox +AxisAlignedBoundingBox::fromPoints(const std::span points) +{ + using Limits = std::numeric_limits; + static constexpr const auto INITIAL + = std::make_pair(GlobalPosition3D {Limits::max()}, GlobalPosition3D {Limits::min()}); + return std::make_from_tuple( + std::ranges::fold_left(points, INITIAL, [](const auto & prev, const auto & point) { + auto & [min, max] = prev; + return std::make_pair(glm::min(min, point), glm::max(max, point)); + })); +} + +AxisAlignedBoundingBox & +AxisAlignedBoundingBox::operator+=(const GlobalPosition3D & point) +{ + min = glm::min(min, point); + max = glm::max(max, point); + return *this; +} diff --git a/gfx/aabb.h b/gfx/aabb.h new file mode 100644 index 0000000..568e91a --- /dev/null +++ b/gfx/aabb.h @@ -0,0 +1,17 @@ +#pragma once + +#include "config/types.h" +#include + +class AxisAlignedBoundingBox { +public: + AxisAlignedBoundingBox() = default; + + AxisAlignedBoundingBox(const GlobalPosition3D & min, const GlobalPosition3D & max) : min {min}, max {max} { } + + AxisAlignedBoundingBox & operator+=(const GlobalPosition3D & point); + + [[nodiscard]] static AxisAlignedBoundingBox fromPoints(std::span points); + + GlobalPosition3D min, max; +}; diff --git a/test/test-lib.cpp b/test/test-lib.cpp index 17c0f63..8dadc70 100644 --- a/test/test-lib.cpp +++ b/test/test-lib.cpp @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -110,3 +111,14 @@ BOOST_DATA_TEST_CASE(mergeCloseInts, tolerance))); BOOST_CHECK_EQUAL_COLCOL(mutableCollection, expected); } + +BOOST_AUTO_TEST_CASE(aabb_from_points) +{ + const auto aabb = AxisAlignedBoundingBox::fromPoints(std::vector { + {1, 2, 3}, + {4, 2, 1}, + {9, 1, 7}, + }); + BOOST_CHECK_EQUAL(aabb.min, GlobalPosition3D(1, 1, 1)); + BOOST_CHECK_EQUAL(aabb.max, GlobalPosition3D(9, 2, 7)); +} -- cgit v1.2.3 From c310240881e9d1b474db6ef8f8f2891ce1646795 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Fri, 7 Mar 2025 00:11:07 +0000 Subject: Position is moved to Frustum --- gfx/camera.cpp | 2 +- gfx/camera.h | 7 ------- gfx/frustum.cpp | 4 ++-- gfx/frustum.h | 10 +++++++++- 4 files changed, 12 insertions(+), 11 deletions(-) (limited to 'gfx') diff --git a/gfx/camera.cpp b/gfx/camera.cpp index cc6a2dd..f01054a 100644 --- a/gfx/camera.cpp +++ b/gfx/camera.cpp @@ -12,7 +12,7 @@ Camera::Camera(GlobalPosition3D pos, Angle fov, Angle aspect, GlobalDistance nea Camera::Camera(GlobalPosition3D pos, GlobalDistance near, GlobalDistance far, const glm::mat4 & view, const glm::mat4 & projection) : - Frustum {view, projection}, position {pos}, forward {::north}, up {::up}, near {near}, far {far} + Frustum {pos, view, projection}, forward {::north}, up {::up}, near {near}, far {far} { } diff --git a/gfx/camera.h b/gfx/camera.h index a52ec8b..d1bfc82 100644 --- a/gfx/camera.h +++ b/gfx/camera.h @@ -59,12 +59,6 @@ public: return forward; } - [[nodiscard]] auto - getPosition() const - { - return position; - } - [[nodiscard]] std::array extentsAtDist(GlobalDistance) const; [[nodiscard]] static Direction3D upFromForward(const Direction3D & forward); @@ -74,7 +68,6 @@ private: const glm::mat4 & projection); void updateView(); - GlobalPosition3D position; Direction3D forward; Direction3D up; GlobalDistance near, far; diff --git a/gfx/frustum.cpp b/gfx/frustum.cpp index 9e046f6..a9dbdf0 100644 --- a/gfx/frustum.cpp +++ b/gfx/frustum.cpp @@ -5,8 +5,8 @@ static constexpr auto PLANES = std::array {0, 1, 2} * std::array {1.F, -1.F}; -Frustum::Frustum(const glm::mat4 & view, const glm::mat4 & projection) : - view {view}, projection {projection}, viewProjection {}, inverseViewProjection {}, planes {} +Frustum::Frustum(const GlobalPosition3D & pos, const glm::mat4 & view, const glm::mat4 & projection) : + position {pos}, view {view}, projection {projection}, viewProjection {}, inverseViewProjection {}, planes {} { updateCache(); } diff --git a/gfx/frustum.h b/gfx/frustum.h index 25dcc18..46f4108 100644 --- a/gfx/frustum.h +++ b/gfx/frustum.h @@ -1,11 +1,12 @@ #pragma once +#include "config/types.h" #include #include class Frustum { public: - Frustum(const glm::mat4 & view, const glm::mat4 & projection); + Frustum(const GlobalPosition3D & pos, const glm::mat4 & view, const glm::mat4 & projection); [[nodiscard]] auto & getFrustumPlanes() const @@ -19,12 +20,19 @@ public: return viewProjection; } + [[nodiscard]] auto + getPosition() const + { + return position; + } + void updateView(const glm::mat4 & view); protected: static constexpr size_t FACES = 6; void updateCache(); + GlobalPosition3D position; glm::mat4 view, projection; glm::mat4 viewProjection, inverseViewProjection; std::array planes; -- cgit v1.2.3 From d0aceb54752078200bc75a96888ffaf7483678cb Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Fri, 7 Mar 2025 00:43:25 +0000 Subject: Add function to test if an AABB is visible in a frustum --- gfx/aabb.cpp | 6 ++++++ gfx/aabb.h | 2 ++ gfx/frustum.cpp | 26 ++++++++++++++++++++++++++ gfx/frustum.h | 4 ++++ 4 files changed, 38 insertions(+) (limited to 'gfx') diff --git a/gfx/aabb.cpp b/gfx/aabb.cpp index 862dacb..0a04ef4 100644 --- a/gfx/aabb.cpp +++ b/gfx/aabb.cpp @@ -22,3 +22,9 @@ AxisAlignedBoundingBox::operator+=(const GlobalPosition3D & point) max = glm::max(max, point); return *this; } + +AxisAlignedBoundingBox +AxisAlignedBoundingBox::operator-(const GlobalPosition3D & viewPoint) const +{ + return {min - viewPoint, max - viewPoint}; +} diff --git a/gfx/aabb.h b/gfx/aabb.h index 568e91a..db3a215 100644 --- a/gfx/aabb.h +++ b/gfx/aabb.h @@ -11,6 +11,8 @@ public: AxisAlignedBoundingBox & operator+=(const GlobalPosition3D & point); + AxisAlignedBoundingBox operator-(const GlobalPosition3D & viewPoint) const; + [[nodiscard]] static AxisAlignedBoundingBox fromPoints(std::span points); GlobalPosition3D min, max; diff --git a/gfx/frustum.cpp b/gfx/frustum.cpp index a9dbdf0..8031d85 100644 --- a/gfx/frustum.cpp +++ b/gfx/frustum.cpp @@ -1,4 +1,5 @@ #include "frustum.h" +#include "aabb.h" #include #include #include @@ -18,6 +19,31 @@ Frustum::updateView(const glm::mat4 & newView) updateCache(); } +bool +Frustum::contains(const AxisAlignedBoundingBox & aabb) const +{ + static constexpr auto EXTENT_CORNER_IDXS = [] { + using Extent = GlobalPosition3D AxisAlignedBoundingBox::*; + constexpr auto EXTENTS = std::array {&AxisAlignedBoundingBox::min, &AxisAlignedBoundingBox::max}; + std::array, 2ZU * 2ZU * 2ZU> out {}; + std::ranges::copy(std::views::cartesian_product(EXTENTS, EXTENTS, EXTENTS) + | std::views::transform( + std::make_from_tuple, std::tuple>), + out.begin()); + return out; + }(); + + const std::array corners + = EXTENT_CORNER_IDXS * [relativeAabb = aabb - position](auto idxs) -> glm::vec4 { + return {(relativeAabb.*(idxs.x)).x, (relativeAabb.*(idxs.y)).y, (relativeAabb.*(idxs.z)).z, 1.F}; + }; + return std::ranges::none_of(planes, [&corners](const auto & frustumPlane) { + return (std::ranges::all_of(corners, [&frustumPlane](const auto & corner) { + return glm::dot(frustumPlane, corner) < 0.F; + })); + }); +} + void Frustum::updateCache() { diff --git a/gfx/frustum.h b/gfx/frustum.h index 46f4108..5ee7d2c 100644 --- a/gfx/frustum.h +++ b/gfx/frustum.h @@ -4,6 +4,8 @@ #include #include +class AxisAlignedBoundingBox; + class Frustum { public: Frustum(const GlobalPosition3D & pos, const glm::mat4 & view, const glm::mat4 & projection); @@ -28,6 +30,8 @@ public: void updateView(const glm::mat4 & view); + [[nodiscard]] bool contains(const AxisAlignedBoundingBox &) const; + protected: static constexpr size_t FACES = 6; void updateCache(); -- cgit v1.2.3 From 9da25631a119fd16f1810a027dae1fde7cb7527d Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Fri, 7 Mar 2025 20:23:53 +0000 Subject: AxisAlignedBoundingBox construct from range instead of span --- gfx/aabb.cpp | 15 --------------- gfx/aabb.h | 16 ++++++++++++++-- 2 files changed, 14 insertions(+), 17 deletions(-) (limited to 'gfx') diff --git a/gfx/aabb.cpp b/gfx/aabb.cpp index 0a04ef4..19c2217 100644 --- a/gfx/aabb.cpp +++ b/gfx/aabb.cpp @@ -1,19 +1,4 @@ #include "aabb.h" -#include -#include - -AxisAlignedBoundingBox -AxisAlignedBoundingBox::fromPoints(const std::span points) -{ - using Limits = std::numeric_limits; - static constexpr const auto INITIAL - = std::make_pair(GlobalPosition3D {Limits::max()}, GlobalPosition3D {Limits::min()}); - return std::make_from_tuple( - std::ranges::fold_left(points, INITIAL, [](const auto & prev, const auto & point) { - auto & [min, max] = prev; - return std::make_pair(glm::min(min, point), glm::max(max, point)); - })); -} AxisAlignedBoundingBox & AxisAlignedBoundingBox::operator+=(const GlobalPosition3D & point) diff --git a/gfx/aabb.h b/gfx/aabb.h index db3a215..229d516 100644 --- a/gfx/aabb.h +++ b/gfx/aabb.h @@ -1,7 +1,8 @@ #pragma once #include "config/types.h" -#include +#include +#include class AxisAlignedBoundingBox { public: @@ -13,7 +14,18 @@ public: AxisAlignedBoundingBox operator-(const GlobalPosition3D & viewPoint) const; - [[nodiscard]] static AxisAlignedBoundingBox fromPoints(std::span points); + [[nodiscard]] static AxisAlignedBoundingBox + fromPoints(auto && points) + { + using Limits = std::numeric_limits; + static constexpr const auto INITIAL + = std::make_pair(GlobalPosition3D {Limits::max()}, GlobalPosition3D {Limits::min()}); + return std::make_from_tuple( + std::ranges::fold_left(points, INITIAL, [](const auto & prev, const auto & point) { + auto & [min, max] = prev; + return std::make_pair(glm::min(min, point), glm::max(max, point)); + })); + } GlobalPosition3D min, max; }; -- cgit v1.2.3 From a6cec1f8eeb54a12fb2ee058f07a451d3b549958 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Sat, 8 Mar 2025 19:54:10 +0000 Subject: Template AxisAlignedBoundingBox on unit type --- game/geoData.h | 2 +- game/terrain.cpp | 2 +- game/terrain.h | 2 +- gfx/aabb.cpp | 15 --------------- gfx/aabb.h | 30 ++++++++++++++++++++---------- gfx/frustum.cpp | 7 +++---- gfx/frustum.h | 7 ++++--- test/test-lib.cpp | 2 +- 8 files changed, 31 insertions(+), 36 deletions(-) delete mode 100644 gfx/aabb.cpp (limited to 'gfx') diff --git a/game/geoData.h b/game/geoData.h index a504f9b..0ff0c42 100644 --- a/game/geoData.h +++ b/game/geoData.h @@ -81,5 +81,5 @@ protected: virtual void afterChange(); private: - AxisAlignedBoundingBox extents; + AxisAlignedBoundingBox extents; }; diff --git a/game/terrain.cpp b/game/terrain.cpp index 530b373..577d9e5 100644 --- a/game/terrain.cpp +++ b/game/terrain.cpp @@ -82,7 +82,7 @@ Terrain::generateMeshes() {(surfaceKey.basePosition + 1) * TILE_SIZE || getExtents().max.z}}; } else { - meshItr->second.aabb = AxisAlignedBoundingBox::fromPoints( + meshItr->second.aabb = AxisAlignedBoundingBox::fromPoints( indices | std::views::transform([this](const auto vertex) -> GlobalPosition3D { return this->point(VertexHandle {static_cast(vertex)}); })); diff --git a/game/terrain.h b/game/terrain.h index f38fe84..5f03634 100644 --- a/game/terrain.h +++ b/game/terrain.h @@ -35,7 +35,7 @@ private: glVertexArray vertexArray; glBuffer indicesBuffer; GLsizei count; - AxisAlignedBoundingBox aabb; + AxisAlignedBoundingBox aabb; }; struct SurfaceKey { diff --git a/gfx/aabb.cpp b/gfx/aabb.cpp deleted file mode 100644 index 19c2217..0000000 --- a/gfx/aabb.cpp +++ /dev/null @@ -1,15 +0,0 @@ -#include "aabb.h" - -AxisAlignedBoundingBox & -AxisAlignedBoundingBox::operator+=(const GlobalPosition3D & point) -{ - min = glm::min(min, point); - max = glm::max(max, point); - return *this; -} - -AxisAlignedBoundingBox -AxisAlignedBoundingBox::operator-(const GlobalPosition3D & viewPoint) const -{ - return {min - viewPoint, max - viewPoint}; -} diff --git a/gfx/aabb.h b/gfx/aabb.h index 229d516..a661f66 100644 --- a/gfx/aabb.h +++ b/gfx/aabb.h @@ -1,31 +1,41 @@ #pragma once -#include "config/types.h" +#include "maths.h" #include #include -class AxisAlignedBoundingBox { +template class AxisAlignedBoundingBox { public: + using V = glm::vec<3, T, Q>; AxisAlignedBoundingBox() = default; - AxisAlignedBoundingBox(const GlobalPosition3D & min, const GlobalPosition3D & max) : min {min}, max {max} { } + AxisAlignedBoundingBox(const V & min, const V & max) : min {min}, max {max} { } - AxisAlignedBoundingBox & operator+=(const GlobalPosition3D & point); + AxisAlignedBoundingBox & + operator+=(const V & point) + { + min = glm::min(min, point); + max = glm::max(max, point); + return *this; + } - AxisAlignedBoundingBox operator-(const GlobalPosition3D & viewPoint) const; + AxisAlignedBoundingBox + operator-(const V & viewPoint) const + { + return {min - viewPoint, max - viewPoint}; + } [[nodiscard]] static AxisAlignedBoundingBox fromPoints(auto && points) { - using Limits = std::numeric_limits; - static constexpr const auto INITIAL - = std::make_pair(GlobalPosition3D {Limits::max()}, GlobalPosition3D {Limits::min()}); - return std::make_from_tuple( + using Limits = std::numeric_limits; + static constexpr const auto INITIAL = std::make_pair(V {Limits::max()}, V {Limits::min()}); + return std::make_from_tuple>( std::ranges::fold_left(points, INITIAL, [](const auto & prev, const auto & point) { auto & [min, max] = prev; return std::make_pair(glm::min(min, point), glm::max(max, point)); })); } - GlobalPosition3D min, max; + V min, max; }; diff --git a/gfx/frustum.cpp b/gfx/frustum.cpp index 8031d85..865dcde 100644 --- a/gfx/frustum.cpp +++ b/gfx/frustum.cpp @@ -1,5 +1,4 @@ #include "frustum.h" -#include "aabb.h" #include #include #include @@ -20,11 +19,11 @@ Frustum::updateView(const glm::mat4 & newView) } bool -Frustum::contains(const AxisAlignedBoundingBox & aabb) const +Frustum::contains(const BoundingBox & aabb) const { static constexpr auto EXTENT_CORNER_IDXS = [] { - using Extent = GlobalPosition3D AxisAlignedBoundingBox::*; - constexpr auto EXTENTS = std::array {&AxisAlignedBoundingBox::min, &AxisAlignedBoundingBox::max}; + using Extent = GlobalPosition3D BoundingBox::*; + constexpr auto EXTENTS = std::array {&BoundingBox::min, &BoundingBox::max}; std::array, 2ZU * 2ZU * 2ZU> out {}; std::ranges::copy(std::views::cartesian_product(EXTENTS, EXTENTS, EXTENTS) | std::views::transform( diff --git a/gfx/frustum.h b/gfx/frustum.h index 5ee7d2c..2624ba1 100644 --- a/gfx/frustum.h +++ b/gfx/frustum.h @@ -1,11 +1,10 @@ #pragma once +#include "aabb.h" #include "config/types.h" #include #include -class AxisAlignedBoundingBox; - class Frustum { public: Frustum(const GlobalPosition3D & pos, const glm::mat4 & view, const glm::mat4 & projection); @@ -30,11 +29,13 @@ public: void updateView(const glm::mat4 & view); - [[nodiscard]] bool contains(const AxisAlignedBoundingBox &) const; + using BoundingBox = AxisAlignedBoundingBox; + [[nodiscard]] bool contains(const BoundingBox &) const; protected: static constexpr size_t FACES = 6; void updateCache(); + [[nodiscard]] bool boundByPlanes(const BoundingBox &, size_t nplanes) const; GlobalPosition3D position; glm::mat4 view, projection; diff --git a/test/test-lib.cpp b/test/test-lib.cpp index 8dadc70..ec91f6e 100644 --- a/test/test-lib.cpp +++ b/test/test-lib.cpp @@ -114,7 +114,7 @@ BOOST_DATA_TEST_CASE(mergeCloseInts, BOOST_AUTO_TEST_CASE(aabb_from_points) { - const auto aabb = AxisAlignedBoundingBox::fromPoints(std::vector { + const auto aabb = AxisAlignedBoundingBox::fromPoints(std::vector { {1, 2, 3}, {4, 2, 1}, {9, 1, 7}, -- cgit v1.2.3 From bf1d591a52428491dfcfe2b5c717adbec9aba768 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Mon, 10 Mar 2025 00:19:04 +0000 Subject: Simplify ShadowMapper with AxisAlignedBoundingBox --- gfx/gl/shadowMapper.cpp | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) (limited to 'gfx') diff --git a/gfx/gl/shadowMapper.cpp b/gfx/gl/shadowMapper.cpp index 908dbdb..231f203 100644 --- a/gfx/gl/shadowMapper.cpp +++ b/gfx/gl/shadowMapper.cpp @@ -1,6 +1,7 @@ #include "shadowMapper.h" #include "collections.h" #include "game/gamestate.h" +#include "gfx/aabb.h" #include "gfx/gl/shaders/fs-shadowDynamicPointInstWithTextures.h" #include "gfx/gl/shaders/fs-shadowDynamicPointStencil.h" #include "gfx/gl/shaders/gs-commonShadowPoint.h" @@ -19,7 +20,6 @@ #include "maths.h" #include "sceneProvider.h" #include "sceneShader.h" -#include "sorting.h" #include #include #include @@ -102,23 +102,16 @@ ShadowMapper::update(const SceneProvider & scene, const LightDirection & dir, co const auto bandViewExtents = getBandViewExtents(camera, lightViewDir); Definitions out; Sizes sizes; - std::transform(bandViewExtents.begin(), std::prev(bandViewExtents.end()), std::next(bandViewExtents.begin()), - std::back_inserter(out), - [bands = bandViewExtents.size() - 2, &lightViewDir, &sizes](const auto & near, const auto & far) mutable { - const auto extents_minmax - = [extents = std::span {near.begin(), far.end()}](auto && comp, RelativeDistance extra) { - const auto mm = std::minmax_element(extents.begin(), extents.end(), comp); - return std::make_pair(comp.get(*mm.first) - extra, comp.get(*mm.second) + extra); - }; - const std::array extents = {extents_minmax(CompareBy {0}, 0), extents_minmax(CompareBy {1}, 0), - extents_minmax(CompareBy {2}, 10'000)}; - - const auto lightProjection = [](const auto & x, const auto & y, const auto & z) { - return glm::ortho(x.first, x.second, y.first, y.second, -z.second, -z.first); - }(extents[0], extents[1], extents[2]); - - sizes.emplace_back(extents[0].second - extents[0].first, extents[1].second - extents[1].first, - extents[2].second - extents[2].first); + using ExtentsBoundingBox = AxisAlignedBoundingBox; + std::ranges::transform(bandViewExtents | std::views::pairwise, std::back_inserter(out), + [&lightViewDir, &sizes](const auto & band) mutable { + const auto & [near, far] = band; + auto extents = ExtentsBoundingBox::fromPoints(std::span {near.begin(), far.end()}); + extents.min.z -= 10'000.F; + extents.max.z += 10'000.F; + const auto lightProjection = glm::ortho( + extents.min.x, extents.max.x, extents.min.y, extents.max.y, -extents.max.z, -extents.min.z); + sizes.emplace_back(extents.max - extents.min); return lightProjection * lightViewDir; }); for (const auto p : std::initializer_list { -- cgit v1.2.3 From 194148abd9ab89a5a514a37b7717b7c4de6aa758 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Mon, 10 Mar 2025 02:12:16 +0000 Subject: Pass a Frustum to shadow renderers The frustum might not be correct at this stage. --- game/scenary/foliage.cpp | 2 +- game/scenary/foliage.h | 2 +- game/terrain.cpp | 2 +- game/terrain.h | 2 +- game/vehicles/railVehicleClass.cpp | 2 +- game/vehicles/railVehicleClass.h | 2 +- gfx/aabb.h | 2 +- gfx/gl/sceneProvider.cpp | 2 +- gfx/gl/sceneProvider.h | 2 +- gfx/gl/shadowMapper.cpp | 9 ++++++++- gfx/renderable.cpp | 2 +- gfx/renderable.h | 2 +- test/test-assetFactory.cpp | 4 ++-- test/test-geoData.cpp | 4 ++-- test/test-render.cpp | 14 +++++++------- ui/gameMainWindow.cpp | 6 +++--- ui/gameMainWindow.h | 2 +- 17 files changed, 34 insertions(+), 27 deletions(-) (limited to 'gfx') diff --git a/game/scenary/foliage.cpp b/game/scenary/foliage.cpp index c1bf9b9..159a078 100644 --- a/game/scenary/foliage.cpp +++ b/game/scenary/foliage.cpp @@ -41,7 +41,7 @@ Foliage::render(const SceneShader & shader, const Frustum &) const } void -Foliage::shadows(const ShadowMapper & mapper) const +Foliage::shadows(const ShadowMapper & mapper, const Frustum &) const { if (const auto count = instances.size()) { const auto dimensions = bodyMesh->getDimensions(); diff --git a/game/scenary/foliage.h b/game/scenary/foliage.h index 422c7aa..71bc734 100644 --- a/game/scenary/foliage.h +++ b/game/scenary/foliage.h @@ -25,7 +25,7 @@ public: mutable InstanceVertices instances; void render(const SceneShader &, const Frustum &) const override; - void shadows(const ShadowMapper &) const override; + void shadows(const ShadowMapper &, const Frustum &) const override; void updateStencil(const ShadowStenciller &) const override; glTexture shadowStencil = ShadowStenciller::createStencilTexture(256, 256); diff --git a/game/terrain.cpp b/game/terrain.cpp index 577d9e5..e9e9463 100644 --- a/game/terrain.cpp +++ b/game/terrain.cpp @@ -128,7 +128,7 @@ Terrain::render(const SceneShader & shader, const Frustum & frustum) const } void -Terrain::shadows(const ShadowMapper & shadowMapper) const +Terrain::shadows(const ShadowMapper & shadowMapper, const Frustum &) const { shadowMapper.landmess.use(); for (const auto & [surface, sab] : meshes) { diff --git a/game/terrain.h b/game/terrain.h index 5f03634..eaec01d 100644 --- a/game/terrain.h +++ b/game/terrain.h @@ -17,7 +17,7 @@ public: } void render(const SceneShader & shader, const Frustum &) const override; - void shadows(const ShadowMapper &) const override; + void shadows(const ShadowMapper &, const Frustum &) const override; void tick(TickDuration) override; diff --git a/game/vehicles/railVehicleClass.cpp b/game/vehicles/railVehicleClass.cpp index 179b570..162a29a 100644 --- a/game/vehicles/railVehicleClass.cpp +++ b/game/vehicles/railVehicleClass.cpp @@ -47,7 +47,7 @@ RailVehicleClass::render(const SceneShader & shader, const Frustum &) const } void -RailVehicleClass::shadows(const ShadowMapper & mapper) const +RailVehicleClass::shadows(const ShadowMapper & mapper, const Frustum &) const { if (const auto count = static_cast(instances.size())) { mapper.dynamicPointInst.use(); diff --git a/game/vehicles/railVehicleClass.h b/game/vehicles/railVehicleClass.h index 2c1fd2b..6eb4ca5 100644 --- a/game/vehicles/railVehicleClass.h +++ b/game/vehicles/railVehicleClass.h @@ -15,7 +15,7 @@ class Location; class RailVehicleClass : public Renderable, public Asset { public: void render(const SceneShader & shader, const Frustum &) const override; - void shadows(const ShadowMapper & shadowMapper) const override; + void shadows(const ShadowMapper & shadowMapper, const Frustum &) const override; struct LocationVertex { glm::mat3 body, front, back; diff --git a/gfx/aabb.h b/gfx/aabb.h index a661f66..ce15a0f 100644 --- a/gfx/aabb.h +++ b/gfx/aabb.h @@ -37,5 +37,5 @@ public: })); } - V min, max; + V min {}, max {}; }; diff --git a/gfx/gl/sceneProvider.cpp b/gfx/gl/sceneProvider.cpp index 4e271db..e01532e 100644 --- a/gfx/gl/sceneProvider.cpp +++ b/gfx/gl/sceneProvider.cpp @@ -9,6 +9,6 @@ SceneProvider::environment(const SceneShader &, const SceneRenderer & renderer) } void -SceneProvider::shadows(const ShadowMapper &) const +SceneProvider::shadows(const ShadowMapper &, const Frustum &) const { } diff --git a/gfx/gl/sceneProvider.h b/gfx/gl/sceneProvider.h index 93b384f..f6b7009 100644 --- a/gfx/gl/sceneProvider.h +++ b/gfx/gl/sceneProvider.h @@ -16,5 +16,5 @@ public: virtual void content(const SceneShader &, const Frustum &) const = 0; virtual void environment(const SceneShader &, const SceneRenderer &) const; virtual void lights(const SceneShader &) const = 0; - virtual void shadows(const ShadowMapper &) const; + virtual void shadows(const ShadowMapper &, const Frustum &) const; }; diff --git a/gfx/gl/shadowMapper.cpp b/gfx/gl/shadowMapper.cpp index 231f203..dc461e0 100644 --- a/gfx/gl/shadowMapper.cpp +++ b/gfx/gl/shadowMapper.cpp @@ -118,7 +118,14 @@ ShadowMapper::update(const SceneProvider & scene, const LightDirection & dir, co &landmess, &dynamicPoint, &dynamicPointInst, &dynamicPointInstWithTextures, &stencilShadowProgram}) { p->setView(out, sizes, lightViewPoint); } - scene.shadows(*this); + ExtentsBoundingBox extents; + for (const auto & point : bandViewExtents.back()) { + extents += point; + } + const auto lightProjection + = glm::ortho(extents.min.x, extents.max.x, extents.min.y, extents.max.y, -extents.max.z, -extents.min.z); + Frustum frustum {lightViewPoint, lightViewDir, lightProjection}; + scene.shadows(*this, frustum); glCullFace(GL_BACK); diff --git a/gfx/renderable.cpp b/gfx/renderable.cpp index 3594968..27f2459 100644 --- a/gfx/renderable.cpp +++ b/gfx/renderable.cpp @@ -6,7 +6,7 @@ Renderable::lights(const SceneShader &) const } void -Renderable::shadows(const ShadowMapper &) const +Renderable::shadows(const ShadowMapper &, const Frustum &) const { } diff --git a/gfx/renderable.h b/gfx/renderable.h index 9fbeccd..140c570 100644 --- a/gfx/renderable.h +++ b/gfx/renderable.h @@ -15,7 +15,7 @@ public: virtual void render(const SceneShader & shader, const Frustum &) const = 0; virtual void lights(const SceneShader & shader) const; - virtual void shadows(const ShadowMapper & shadowMapper) const; + virtual void shadows(const ShadowMapper & shadowMapper, const Frustum &) const; virtual void updateStencil(const ShadowStenciller & lightDir) const; }; diff --git a/test/test-assetFactory.cpp b/test/test-assetFactory.cpp index 02f0202..9bade82 100644 --- a/test/test-assetFactory.cpp +++ b/test/test-assetFactory.cpp @@ -58,10 +58,10 @@ public: } void - shadows(const ShadowMapper & mapper) const override + shadows(const ShadowMapper & mapper, const Frustum & frustum) const override { mapper.dynamicPoint.use(Location {{0, 0, 0}, {0, 0, 0}}); - objects.apply(&Renderable::shadows, mapper); + objects.apply(&Renderable::shadows, mapper, frustum); } void diff --git a/test/test-geoData.cpp b/test/test-geoData.cpp index fb8b5c5..e3ef9ad 100644 --- a/test/test-geoData.cpp +++ b/test/test-geoData.cpp @@ -273,9 +273,9 @@ BOOST_DATA_TEST_CASE(deform, loadFixtureJson("geoData/deform/ } void - shadows(const ShadowMapper & shadowMapper) const override + shadows(const ShadowMapper & shadowMapper, const Frustum & frustum) const override { - terrain.shadows(shadowMapper); + terrain.shadows(shadowMapper, frustum); } }; diff --git a/test/test-render.cpp b/test/test-render.cpp index 080e635..f205b89 100644 --- a/test/test-render.cpp +++ b/test/test-render.cpp @@ -92,12 +92,12 @@ public: } void - shadows(const ShadowMapper & shadowMapper) const override + shadows(const ShadowMapper & shadowMapper, const Frustum & frustum) const override { - terrain->shadows(shadowMapper); - std::ranges::for_each(gameState->assets, [&shadowMapper](const auto & asset) { + terrain->shadows(shadowMapper, frustum); + std::ranges::for_each(gameState->assets, [&shadowMapper, &frustum](const auto & asset) { if (const auto renderable = std::dynamic_pointer_cast(asset.second)) { - renderable->shadows(shadowMapper); + renderable->shadows(shadowMapper, frustum); } }); } @@ -190,9 +190,9 @@ BOOST_AUTO_TEST_CASE(terrain) } void - shadows(const ShadowMapper & shadowMapper) const override + shadows(const ShadowMapper & shadowMapper, const Frustum & frustum) const override { - terrain->shadows(shadowMapper); + terrain->shadows(shadowMapper, frustum); } }; @@ -237,7 +237,7 @@ BOOST_AUTO_TEST_CASE(railnet) } void - shadows(const ShadowMapper &) const override + shadows(const ShadowMapper &, const Frustum &) const override { } }; diff --git a/ui/gameMainWindow.cpp b/ui/gameMainWindow.cpp index 3403afa..f63137c 100644 --- a/ui/gameMainWindow.cpp +++ b/ui/gameMainWindow.cpp @@ -78,12 +78,12 @@ GameMainWindow::lights(const SceneShader & shader) const } void -GameMainWindow::shadows(const ShadowMapper & shadowMapper) const +GameMainWindow::shadows(const ShadowMapper & shadowMapper, const Frustum & frustum) const { for (const auto & [id, asset] : gameState->assets) { if (const auto r = std::dynamic_pointer_cast(asset)) { - r->shadows(shadowMapper); + r->shadows(shadowMapper, frustum); } } - gameState->world.apply(&Renderable::shadows, shadowMapper); + gameState->world.apply(&Renderable::shadows, shadowMapper, frustum); } diff --git a/ui/gameMainWindow.h b/ui/gameMainWindow.h index 112d23b..43980e8 100644 --- a/ui/gameMainWindow.h +++ b/ui/gameMainWindow.h @@ -20,5 +20,5 @@ private: void content(const SceneShader &, const Frustum &) const override; void environment(const SceneShader &, const SceneRenderer &) const override; void lights(const SceneShader &) const override; - void shadows(const ShadowMapper &) const override; + void shadows(const ShadowMapper &, const Frustum &) const override; }; -- cgit v1.2.3 From fc74736b9eaa1f3e033ded9103b69d2a39a3e263 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Tue, 11 Mar 2025 00:40:35 +0000 Subject: Extend Frustum for testing for shaded by Like contains, but doesn't test the back plane as shadow caster can be anywhere behind the view point and still cast into it. --- gfx/frustum.cpp | 16 ++++++++++++++-- gfx/frustum.h | 1 + 2 files changed, 15 insertions(+), 2 deletions(-) (limited to 'gfx') diff --git a/gfx/frustum.cpp b/gfx/frustum.cpp index 865dcde..faa676d 100644 --- a/gfx/frustum.cpp +++ b/gfx/frustum.cpp @@ -3,7 +3,7 @@ #include #include -static constexpr auto PLANES = std::array {0, 1, 2} * std::array {1.F, -1.F}; +static constexpr auto PLANES = std::array {0, 1, 2} * std::array {-1.F, 1.F}; Frustum::Frustum(const GlobalPosition3D & pos, const glm::mat4 & view, const glm::mat4 & projection) : position {pos}, view {view}, projection {projection}, viewProjection {}, inverseViewProjection {}, planes {} @@ -20,6 +20,18 @@ Frustum::updateView(const glm::mat4 & newView) bool Frustum::contains(const BoundingBox & aabb) const +{ + return boundByPlanes(aabb, FACES); +} + +bool +Frustum::shadedBy(const BoundingBox & aabb) const +{ + return boundByPlanes(aabb, FACES - 1); +} + +bool +Frustum::boundByPlanes(const BoundingBox & aabb, size_t nplanes) const { static constexpr auto EXTENT_CORNER_IDXS = [] { using Extent = GlobalPosition3D BoundingBox::*; @@ -36,7 +48,7 @@ Frustum::contains(const BoundingBox & aabb) const = EXTENT_CORNER_IDXS * [relativeAabb = aabb - position](auto idxs) -> glm::vec4 { return {(relativeAabb.*(idxs.x)).x, (relativeAabb.*(idxs.y)).y, (relativeAabb.*(idxs.z)).z, 1.F}; }; - return std::ranges::none_of(planes, [&corners](const auto & frustumPlane) { + return std::ranges::none_of(std::span(planes).subspan(0, nplanes), [&corners](const auto & frustumPlane) { return (std::ranges::all_of(corners, [&frustumPlane](const auto & corner) { return glm::dot(frustumPlane, corner) < 0.F; })); diff --git a/gfx/frustum.h b/gfx/frustum.h index 2624ba1..a2d90e9 100644 --- a/gfx/frustum.h +++ b/gfx/frustum.h @@ -31,6 +31,7 @@ public: using BoundingBox = AxisAlignedBoundingBox; [[nodiscard]] bool contains(const BoundingBox &) const; + [[nodiscard]] bool shadedBy(const BoundingBox &) const; protected: static constexpr size_t FACES = 6; -- cgit v1.2.3 From 49b5e522e64020c2ce41e3dd86de7c87680d180d Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Tue, 11 Mar 2025 00:49:06 +0000 Subject: Initialise shadow bounding box to light view point Then extended to cover the view extents. --- gfx/gl/shadowMapper.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'gfx') diff --git a/gfx/gl/shadowMapper.cpp b/gfx/gl/shadowMapper.cpp index dc461e0..28ef0ae 100644 --- a/gfx/gl/shadowMapper.cpp +++ b/gfx/gl/shadowMapper.cpp @@ -118,7 +118,7 @@ ShadowMapper::update(const SceneProvider & scene, const LightDirection & dir, co &landmess, &dynamicPoint, &dynamicPointInst, &dynamicPointInstWithTextures, &stencilShadowProgram}) { p->setView(out, sizes, lightViewPoint); } - ExtentsBoundingBox extents; + ExtentsBoundingBox extents {lightViewPoint, lightViewPoint}; for (const auto & point : bandViewExtents.back()) { extents += point; } -- cgit v1.2.3 From d0ea1598abdb55c98ca8daea857f6fca77119928 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Tue, 11 Mar 2025 00:59:34 +0000 Subject: Rebalance shadow band distribution Extends the initial band from around 34m to around 100m. --- gfx/gl/shadowMapper.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'gfx') diff --git a/gfx/gl/shadowMapper.cpp b/gfx/gl/shadowMapper.cpp index 28ef0ae..6525f76 100644 --- a/gfx/gl/shadowMapper.cpp +++ b/gfx/gl/shadowMapper.cpp @@ -56,7 +56,7 @@ constexpr auto shadowBands = [](const float scaleFactor, std::integer_sequence) { const auto base = 10'000'000 / pow(scaleFactor, sizeof...(ints) - 1); return std::array {1, static_cast((base * pow(scaleFactor, ints)))...}; - }(6.6F, std::make_integer_sequence()); + }(4.6F, std::make_integer_sequence()); static_assert(shadowBands.front() == 1); static_assert(shadowBands.back() == 10'000'000); -- cgit v1.2.3