summaryrefslogtreecommitdiff
path: root/gfx
diff options
context:
space:
mode:
Diffstat (limited to 'gfx')
-rw-r--r--gfx/aabb.h41
-rw-r--r--gfx/camera.cpp (renamed from gfx/gl/camera.cpp)44
-rw-r--r--gfx/camera.h (renamed from gfx/gl/camera.h)39
-rw-r--r--gfx/followCameraController.cpp7
-rw-r--r--gfx/frustum.cpp71
-rw-r--r--gfx/frustum.h52
-rw-r--r--gfx/gl/billboardPainter.cpp90
-rw-r--r--gfx/gl/billboardPainter.h29
-rw-r--r--gfx/gl/bufferedLocation.cpp70
-rw-r--r--gfx/gl/bufferedLocation.h48
-rw-r--r--gfx/gl/glBuffer.h24
-rw-r--r--gfx/gl/glFramebuffer.cpp28
-rw-r--r--gfx/gl/glFramebuffer.h40
-rw-r--r--gfx/gl/glTexture.cpp195
-rw-r--r--gfx/gl/glTexture.h72
-rw-r--r--gfx/gl/glVertexArray.h162
-rw-r--r--gfx/gl/gldebug.cpp20
-rw-r--r--gfx/gl/gldebug.h52
-rw-r--r--gfx/gl/instanceVertices.h111
-rw-r--r--gfx/gl/program.cpp3
-rw-r--r--gfx/gl/program.h11
-rw-r--r--gfx/gl/sceneProvider.cpp4
-rw-r--r--gfx/gl/sceneProvider.h9
-rw-r--r--gfx/gl/sceneRenderer.cpp194
-rw-r--r--gfx/gl/sceneRenderer.h27
-rw-r--r--gfx/gl/sceneShader.cpp79
-rw-r--r--gfx/gl/sceneShader.h24
-rw-r--r--gfx/gl/shader.cpp20
-rw-r--r--gfx/gl/shaders/billboard.frag29
-rw-r--r--gfx/gl/shaders/billboard.vert27
-rw-r--r--gfx/gl/shaders/billboardPainter.frag23
-rw-r--r--gfx/gl/shaders/billboardPainter.geom35
-rw-r--r--gfx/gl/shaders/billboardPainter.vert24
-rw-r--r--gfx/gl/shaders/commonLocationData.glsl20
-rw-r--r--gfx/gl/shaders/commonPoint.glsl11
-rw-r--r--gfx/gl/shaders/commonShadowPoint-geom.glsl (renamed from gfx/gl/shaders/commonShadowPoint.gs)24
-rw-r--r--gfx/gl/shaders/commonShadowPoint.geom4
-rw-r--r--gfx/gl/shaders/commonShadowPoint.glsl11
-rw-r--r--gfx/gl/shaders/directionalLight.frag59
-rw-r--r--gfx/gl/shaders/directionalLight.fs50
-rw-r--r--gfx/gl/shaders/dynamicPoint.vert (renamed from gfx/gl/shaders/dynamicPoint.vs)10
-rw-r--r--gfx/gl/shaders/dynamicPointInst.vert16
-rw-r--r--gfx/gl/shaders/dynamicPointInst.vs14
-rw-r--r--gfx/gl/shaders/fixedPoint.vert (renamed from gfx/gl/shaders/fixedPoint.vs)10
-rw-r--r--gfx/gl/shaders/getMaterialDetail.glsl7
-rw-r--r--gfx/gl/shaders/landmass.frag (renamed from gfx/gl/shaders/landmass.fs)14
-rw-r--r--gfx/gl/shaders/landmass.vert (renamed from gfx/gl/shaders/landmass.vs)6
-rw-r--r--gfx/gl/shaders/lighting.frag (renamed from gfx/gl/shaders/lighting.fs)3
-rw-r--r--gfx/gl/shaders/lighting.vert (renamed from gfx/gl/shaders/lighting.vs)2
-rw-r--r--gfx/gl/shaders/material.frag (renamed from gfx/gl/shaders/material.fs)10
-rw-r--r--gfx/gl/shaders/materialCommon.glsl9
-rw-r--r--gfx/gl/shaders/materialDetail.glsl5
-rw-r--r--gfx/gl/shaders/materialInterface.glsl13
-rw-r--r--gfx/gl/shaders/materialOut.glsl7
-rw-r--r--gfx/gl/shaders/meshIn.glsl5
-rw-r--r--gfx/gl/shaders/network.frag (renamed from gfx/gl/shaders/network.fs)10
-rw-r--r--gfx/gl/shaders/networkCommon.glsl60
-rw-r--r--gfx/gl/shaders/networkCurve.geom18
-rw-r--r--gfx/gl/shaders/networkCurve.gs47
-rw-r--r--gfx/gl/shaders/networkCurve.tesc37
-rw-r--r--gfx/gl/shaders/networkCurve.tese46
-rw-r--r--gfx/gl/shaders/networkCurve.vert23
-rw-r--r--gfx/gl/shaders/networkCurve.vs29
-rw-r--r--gfx/gl/shaders/networkStraight.geom18
-rw-r--r--gfx/gl/shaders/networkStraight.gs17
-rw-r--r--gfx/gl/shaders/networkStraight.vert22
-rw-r--r--gfx/gl/shaders/networkStraight.vs24
-rw-r--r--gfx/gl/shaders/pointLight.frag (renamed from gfx/gl/shaders/pointLight.fs)5
-rw-r--r--gfx/gl/shaders/pointLight.geom (renamed from gfx/gl/shaders/pointLight.gs)4
-rw-r--r--gfx/gl/shaders/pointLight.vert28
-rw-r--r--gfx/gl/shaders/pointLight.vs24
-rw-r--r--gfx/gl/shaders/shadowDynamicPoint.vert10
-rw-r--r--gfx/gl/shaders/shadowDynamicPoint.vs9
-rw-r--r--gfx/gl/shaders/shadowDynamicPointInst.vert12
-rw-r--r--gfx/gl/shaders/shadowDynamicPointInst.vs9
-rw-r--r--gfx/gl/shaders/shadowDynamicPointInstWithTextures.frag (renamed from gfx/gl/shaders/shadowDynamicPointInstWithTextures.fs)8
-rw-r--r--gfx/gl/shaders/shadowDynamicPointInstWithTextures.geom5
-rw-r--r--gfx/gl/shaders/shadowDynamicPointInstWithTextures.gs3
-rw-r--r--gfx/gl/shaders/shadowDynamicPointInstWithTextures.vert17
-rw-r--r--gfx/gl/shaders/shadowDynamicPointInstWithTextures.vs15
-rw-r--r--gfx/gl/shaders/shadowDynamicPointStencil.frag15
-rw-r--r--gfx/gl/shaders/shadowDynamicPointStencil.geom35
-rw-r--r--gfx/gl/shaders/shadowDynamicPointStencil.vert18
-rw-r--r--gfx/gl/shaders/shadowLandmass.vert (renamed from gfx/gl/shaders/shadowLandmass.vs)2
-rw-r--r--gfx/gl/shaders/shadowStencil.frag19
-rw-r--r--gfx/gl/shaders/shadowStencil.geom28
-rw-r--r--gfx/gl/shaders/shadowStencil.vert20
-rw-r--r--gfx/gl/shaders/spotLight.frag (renamed from gfx/gl/shaders/spotLight.fs)5
-rw-r--r--gfx/gl/shaders/spotLight.geom (renamed from gfx/gl/shaders/spotLight.gs)4
-rw-r--r--gfx/gl/shaders/spotLight.vert (renamed from gfx/gl/shaders/spotLight.vs)14
-rw-r--r--gfx/gl/shaders/uiShader.fs11
-rw-r--r--gfx/gl/shaders/uiShader.vs13
-rw-r--r--gfx/gl/shaders/uiShaderFont.fs12
-rw-r--r--gfx/gl/shaders/water.frag18
-rw-r--r--gfx/gl/shaders/water.fs17
-rw-r--r--gfx/gl/shaders/water.vert (renamed from gfx/gl/shaders/water.vs)8
-rw-r--r--gfx/gl/shadowMapper.cpp192
-rw-r--r--gfx/gl/shadowMapper.h48
-rw-r--r--gfx/gl/shadowStenciller.cpp75
-rw-r--r--gfx/gl/shadowStenciller.h28
-rw-r--r--gfx/gl/uiShader.cpp27
-rw-r--r--gfx/gl/uiShader.h47
-rw-r--r--gfx/gl/vertexArrayObject.h117
-rw-r--r--gfx/lightDirection.cpp25
-rw-r--r--gfx/lightDirection.h46
-rw-r--r--gfx/models/lights.cpp11
-rw-r--r--gfx/models/lights.h29
-rw-r--r--gfx/models/mesh.cpp43
-rw-r--r--gfx/models/mesh.h69
-rw-r--r--gfx/models/texture.cpp129
-rw-r--r--gfx/models/texture.h25
-rw-r--r--gfx/models/tga.h9
-rw-r--r--gfx/models/vertex.cpp8
-rw-r--r--gfx/renderable.cpp93
-rw-r--r--gfx/renderable.h49
115 files changed, 2704 insertions, 1119 deletions
diff --git a/gfx/aabb.h b/gfx/aabb.h
new file mode 100644
index 0000000..ce15a0f
--- /dev/null
+++ b/gfx/aabb.h
@@ -0,0 +1,41 @@
+#pragma once
+
+#include "maths.h"
+#include <algorithm>
+#include <tuple>
+
+template<Arithmetic T, glm::qualifier Q = glm::defaultp> class AxisAlignedBoundingBox {
+public:
+ using V = glm::vec<3, T, Q>;
+ AxisAlignedBoundingBox() = default;
+
+ AxisAlignedBoundingBox(const V & min, const V & max) : min {min}, max {max} { }
+
+ AxisAlignedBoundingBox &
+ operator+=(const V & point)
+ {
+ min = glm::min(min, point);
+ max = glm::max(max, point);
+ return *this;
+ }
+
+ AxisAlignedBoundingBox
+ operator-(const V & viewPoint) const
+ {
+ return {min - viewPoint, max - viewPoint};
+ }
+
+ [[nodiscard]] static AxisAlignedBoundingBox
+ fromPoints(auto && points)
+ {
+ using Limits = std::numeric_limits<T>;
+ static constexpr const auto INITIAL = std::make_pair(V {Limits::max()}, V {Limits::min()});
+ return std::make_from_tuple<AxisAlignedBoundingBox<T, Q>>(
+ 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));
+ }));
+ }
+
+ V min {}, max {};
+};
diff --git a/gfx/gl/camera.cpp b/gfx/camera.cpp
index 82b11a6..4d46810 100644
--- a/gfx/gl/camera.cpp
+++ b/gfx/camera.cpp
@@ -4,28 +4,45 @@
#include <maths.h>
#include <ray.h>
-Camera::Camera(GlobalPosition3D pos, Angle fov, Angle aspect, GlobalDistance zNear, GlobalDistance zFar) :
- position {pos}, forward {::north}, up {::up}, near {zNear}, far {zFar},
- projection {
- glm::perspective(fov, aspect, static_cast<RelativeDistance>(zNear), static_cast<RelativeDistance>(zFar))},
- viewProjection {}, inverseViewProjection {}
+Camera::Camera(GlobalPosition3D pos, Angle fov, Angle aspect, RelativeDistance near, RelativeDistance far) :
+ Camera {pos, fov, aspect, near, far, glm::lookAt({}, ::north, ::up),
+ glm::perspective(fov, aspect, static_cast<RelativeDistance>(near), static_cast<RelativeDistance>(far))}
{
- updateView();
+}
+
+Camera::Camera(GlobalPosition3D pos, Angle fov, Angle aspect, RelativeDistance near, RelativeDistance far,
+ const glm::mat4 & view, const glm::mat4 & projection) :
+ Frustum {pos, view, projection}, fov {fov}, aspect {aspect}, forward {::north}, up {::up}, near {near}, far {far}
+{
+}
+
+void
+Camera::setAspect(Angle aspect)
+{
+ projection = glm::perspective(fov, aspect, static_cast<RelativeDistance>(near), static_cast<RelativeDistance>(far));
+ Frustum::updateCache();
+}
+
+Angle
+Camera::getAspect() const
+{
+ return aspect;
}
Ray<GlobalPosition3D>
Camera::unProject(const ScreenRelCoord & mouse) const
{
- static constexpr const glm::vec4 screen {0, 0, 1, 1};
- const auto mouseProjection = glm::lookAt({}, forward, up);
- return {position, glm::normalize(glm::unProject(mouse || 1.F, mouseProjection, projection, screen))};
+ static constexpr const glm::vec4 SCREEN {0, 0, 1, 1};
+ return {
+ .start = position,
+ .direction = glm::normalize(glm::unProject(mouse || 1.F, view, projection, SCREEN)),
+ };
}
void
Camera::updateView()
{
- viewProjection = projection * glm::lookAt({}, forward, up);
- inverseViewProjection = glm::inverse(viewProjection);
+ Frustum::updateView(glm::lookAt({}, forward, up));
}
Direction3D
@@ -47,9 +64,8 @@ Camera::extentsAtDist(const GlobalDistance dist) const
}
return {target, dist};
};
- const auto depth = -(2.F * (static_cast<float>(dist - near)) * static_cast<float>(far))
- / (static_cast<float>(dist) * (static_cast<float>(near - far)))
- - 1.F;
+ const auto depth
+ = -(2.F * (static_cast<float>(dist) - near) * far) / (static_cast<float>(dist) * (near - far)) - 1.F;
static constexpr const std::array extents {-1.F, 1.F};
static constexpr const auto cartesianExtents = extents * extents;
return cartesianExtents * [&depth, this, &clampToSeaFloor](const auto & extent) {
diff --git a/gfx/gl/camera.h b/gfx/camera.h
index 8d53261..1be012b 100644
--- a/gfx/gl/camera.h
+++ b/gfx/camera.h
@@ -1,22 +1,20 @@
#pragma once
#include "config/types.h"
+#include "frustum.h"
#include <glm/glm.hpp>
#include <maths.h>
#include <ray.h>
-class Camera {
+class Camera : public Frustum {
public:
- Camera(GlobalPosition3D, Angle fov, Angle aspect, GlobalDistance zNear, GlobalDistance zFar);
-
- [[nodiscard]] glm::mat4
- getViewProjection() const
- {
- return viewProjection;
- }
+ Camera(GlobalPosition3D position, Angle fov, Angle aspect, RelativeDistance near, RelativeDistance far);
[[nodiscard]] Ray<GlobalPosition3D> unProject(const ScreenRelCoord &) const;
+ void setAspect(Angle aspect);
+ Angle getAspect() const;
+
void
setPosition(const GlobalPosition3D & p)
{
@@ -65,9 +63,21 @@ public:
}
[[nodiscard]] auto
- getPosition() const
+ getNear() const
{
- return position;
+ return near;
+ }
+
+ [[nodiscard]] auto
+ getFar() const
+ {
+ return far;
+ }
+
+ [[nodiscard]] auto
+ getDepth() const
+ {
+ return far - near;
}
[[nodiscard]] std::array<GlobalPosition4D, 4> extentsAtDist(GlobalDistance) const;
@@ -75,13 +85,12 @@ public:
[[nodiscard]] static Direction3D upFromForward(const Direction3D & forward);
private:
+ Camera(GlobalPosition3D position, Angle fov, Angle aspect, RelativeDistance near, RelativeDistance far,
+ const glm::mat4 & view, const glm::mat4 & projection);
void updateView();
- GlobalPosition3D position;
+ Angle fov, aspect;
Direction3D forward;
Direction3D up;
-
- GlobalDistance near, far;
- glm::mat4 projection;
- glm::mat4 viewProjection, inverseViewProjection;
+ RelativeDistance near, far;
};
diff --git a/gfx/followCameraController.cpp b/gfx/followCameraController.cpp
index 52dfb35..c3a5d08 100644
--- a/gfx/followCameraController.cpp
+++ b/gfx/followCameraController.cpp
@@ -1,10 +1,9 @@
#include "followCameraController.h"
#include "game/vehicles/vehicle.h"
-#include <gfx/gl/camera.h>
+#include <gfx/camera.h>
#include <glm/glm.hpp>
#include <location.h>
#include <maths.h>
-#include <memory>
#include <tuple>
#include <utility>
@@ -15,7 +14,7 @@ FollowCameraController::updateCamera(Camera * camera) const
{
const auto [pos, rot] = [this]() {
const auto t {target.lock()};
- return std::tie(t->getLocation().pos, t->getLocation().rot);
+ return std::make_pair(t->getLocation().pos, t->getLocation().rot);
}();
switch (mode) {
@@ -24,7 +23,7 @@ FollowCameraController::updateCamera(Camera * camera) const
break;
case Mode::Ride:
- camera->setView(pos + (up * 4.8F), -sincosf(rot.y) || 0.F);
+ camera->setView(pos + (up * 4.8F), -sincos(rot.y) || 0.F);
break;
case Mode::ISO:
diff --git a/gfx/frustum.cpp b/gfx/frustum.cpp
new file mode 100644
index 0000000..294fe5d
--- /dev/null
+++ b/gfx/frustum.cpp
@@ -0,0 +1,71 @@
+#include "frustum.h"
+#include <algorithm>
+#include <collections.h>
+#include <glm/ext/matrix_transform.hpp>
+
+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 {}
+{
+ updateCache();
+}
+
+void
+Frustum::updateView(const glm::mat4 & newView)
+{
+ view = newView;
+ updateCache();
+}
+
+bool
+Frustum::contains(const BoundingBox & aabb) const
+{
+ static constexpr auto EXTENT_CORNER_IDXS = [] {
+ using Extent = GlobalPosition3D BoundingBox::*;
+ constexpr auto EXTENTS = std::array {&BoundingBox::min, &BoundingBox::max};
+ std::array<glm::vec<3, Extent>, 2ZU * 2ZU * 2ZU> out {};
+ std::ranges::copy(std::views::cartesian_product(EXTENTS, EXTENTS, EXTENTS)
+ | std::views::transform(
+ std::make_from_tuple<glm::vec<3, Extent>, std::tuple<Extent, Extent, Extent>>),
+ out.begin());
+ return out;
+ }();
+
+ const std::array<RelativePosition4D, 8> 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 contains(corners, 0);
+}
+
+bool
+Frustum::contains(GlobalPosition3D point, RelativeDistance size) const
+{
+ return contains(std::array {RelativePosition4D {(point - position), 1.F}}, size);
+}
+
+bool
+Frustum::contains(const std::span<const RelativePosition4D> points, RelativeDistance size) const
+{
+ return std::ranges::none_of(planes, [&points, size](const auto & frustumPlane) {
+ return (std::ranges::all_of(points, [&frustumPlane, size](const auto & point) {
+ const auto distanceFromPlane = glm::dot(frustumPlane, point);
+ return distanceFromPlane < -size;
+ }));
+ });
+}
+
+void
+Frustum::updateCache()
+{
+ viewProjection = projection * view;
+ inverseViewProjection = glm::inverse(viewProjection);
+ std::ranges::transform(PLANES | std::views::take(planes.size()), planes.begin(),
+ [vpt = glm::transpose(viewProjection)](const auto & idxs) {
+ const auto [idx, sgn] = idxs;
+ const auto plane = vpt[3] + (vpt[idx] * sgn);
+ const auto mag = glm::length(plane.xyz());
+ return plane / mag;
+ });
+}
diff --git a/gfx/frustum.h b/gfx/frustum.h
new file mode 100644
index 0000000..5b7947b
--- /dev/null
+++ b/gfx/frustum.h
@@ -0,0 +1,52 @@
+#pragma once
+
+#include "aabb.h"
+#include "config/types.h"
+#include <array>
+#include <glm/mat4x4.hpp>
+#include <span>
+
+class Frustum {
+public:
+ Frustum(const GlobalPosition3D & pos, const glm::mat4 & view, const glm::mat4 & projection);
+
+ [[nodiscard]] auto &
+ getFrustumPlanes() const
+ {
+ return planes;
+ }
+
+ [[nodiscard]] auto &
+ getView() const
+ {
+ return view;
+ }
+
+ [[nodiscard]] auto &
+ getViewProjection() const
+ {
+ return viewProjection;
+ }
+
+ [[nodiscard]] auto
+ getPosition() const
+ {
+ return position;
+ }
+
+ void updateView(const glm::mat4 & view);
+
+ using BoundingBox = AxisAlignedBoundingBox<GlobalDistance>;
+ [[nodiscard]] bool contains(const BoundingBox &) const;
+ [[nodiscard]] bool contains(GlobalPosition3D, RelativeDistance size = 0) const;
+
+protected:
+ static constexpr size_t FACES = 5;
+ void updateCache();
+ [[nodiscard]] bool contains(std::span<const RelativePosition4D>, RelativeDistance) const;
+
+ GlobalPosition3D position;
+ glm::mat4 view, projection;
+ glm::mat4 viewProjection, inverseViewProjection;
+ std::array<glm::vec4, FACES> planes;
+};
diff --git a/gfx/gl/billboardPainter.cpp b/gfx/gl/billboardPainter.cpp
new file mode 100644
index 0000000..878e950
--- /dev/null
+++ b/gfx/gl/billboardPainter.cpp
@@ -0,0 +1,90 @@
+#include "billboardPainter.h"
+#include "gldebug.h"
+#include "maths.h"
+#include <gfx/gl/shaders/billboardPainter-frag.h>
+#include <gfx/gl/shaders/billboardPainter-geom.h>
+#include <gfx/gl/shaders/billboardPainter-vert.h>
+#include <stdexcept>
+
+const auto VIEWS = []<GLint... Ep>(std::integer_sequence<GLint, Ep...>) {
+ constexpr float STEP = two_pi / BillboardPainter::VIEW_ANGLES<decltype(two_pi)>;
+ return std::array {rotate_yaw<4>(Ep * STEP)...};
+}(std::make_integer_sequence<GLint, BillboardPainter::VIEW_ANGLES<GLint>>());
+
+BillboardPainter::BillboardPainter() :
+ program {billboardPainter_vert, billboardPainter_geom, billboardPainter_frag}, angle {}, view {}
+{
+ glDebugScope _ {fbo};
+ glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+ static constexpr std::array<GLenum, 2> ATTACHMENTS {GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1};
+ glDrawBuffers(ATTACHMENTS.size(), ATTACHMENTS.data());
+ glReadBuffer(GL_NONE);
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+ glUseProgram(program);
+ glUniform(viewLoc, std::span<const glm::mat4> {VIEWS});
+}
+
+void
+BillboardPainter::setView(const Angle newAngle, const glm::mat4 & newView)
+{
+ angle = newAngle;
+ view = newView;
+}
+
+Angle
+BillboardPainter::getAngle() const
+{
+ return angle;
+}
+
+void
+BillboardPainter::configureBillBoardTextures(glTextures<GL_TEXTURE_2D_ARRAY, 3> & textures, ImageDimensions size)
+{
+ glDebugScope _ {0};
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+
+ const auto configuregdata = [size](Impl::glTexture<GL_TEXTURE_2D_ARRAY> & texture, const GLenum iformat) {
+ texture.storage(1, iformat, size || VIEW_ANGLES<GLsizei>);
+ texture.parameter(GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ texture.parameter(GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ texture.parameter(GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ texture.parameter(GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ };
+ configuregdata(textures[0], GL_DEPTH_COMPONENT16);
+ configuregdata(textures[1], GL_RGB8_SNORM);
+ configuregdata(textures[2], GL_RGB5_A1);
+}
+
+void
+BillboardPainter::renderBillBoard(const glTextures<GL_TEXTURE_2D_ARRAY, 3> & billboard, const MeshBase & mesh,
+ const Texture::AnyPtr texture) const
+{
+ glDebugScope _ {fbo};
+ glEnable(GL_BLEND);
+ glEnable(GL_DEPTH_TEST);
+ glDisable(GL_CULL_FACE);
+ glCullFace(GL_BACK);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+ glClearColor(0, 0, 0, 0);
+ fbo.texture(GL_DEPTH_ATTACHMENT, billboard[0]);
+ fbo.texture(GL_COLOR_ATTACHMENT0, billboard[1]);
+ fbo.texture(GL_COLOR_ATTACHMENT1, billboard[2]);
+ fbo.assertComplete();
+ if (texture) {
+ texture->bind(0);
+ }
+ glUseProgram(program);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+ const TextureDimensions billboardSize = billboard[0].getSize();
+ glViewport(0, 0, billboardSize.x, billboardSize.y);
+ const auto & centre = mesh.getDimensions().centre;
+ const auto & size = mesh.getDimensions().size;
+ glUniform(viewProjectionLoc,
+ std::span<const glm::mat4> {VIEWS *
+ [extentsMat = glm::translate(glm::ortho(-size, size, -size, size, -size, size), -centre), this](
+ const auto & view) {
+ return this->view * view * extentsMat;
+ }});
+ mesh.draw();
+}
diff --git a/gfx/gl/billboardPainter.h b/gfx/gl/billboardPainter.h
new file mode 100644
index 0000000..617f431
--- /dev/null
+++ b/gfx/gl/billboardPainter.h
@@ -0,0 +1,29 @@
+#pragma once
+
+#include "gfx/models/mesh.h"
+#include "gfx/models/texture.h"
+#include "glFramebuffer.h"
+#include "program.h"
+
+class LightDirection;
+
+class BillboardPainter {
+public:
+ template<typename T> static constexpr T VIEW_ANGLES = 8;
+
+ BillboardPainter();
+
+ static void configureBillBoardTextures(glTextures<GL_TEXTURE_2D_ARRAY, 3> &, ImageDimensions);
+ void setView(Angle angle, const glm::mat4 &);
+ [[nodiscard]] Angle getAngle() const;
+ void renderBillBoard(const glTextures<GL_TEXTURE_2D_ARRAY, 3> &, const MeshBase &, Texture::AnyPtr texture) const;
+
+private:
+ mutable glFramebuffer fbo;
+ Program program;
+ Program::RequiredUniformLocation viewProjectionLoc {program, "viewProjection"};
+ Program::RequiredUniformLocation viewLoc {program, "view"};
+
+ Angle angle;
+ glm::mat4 view;
+};
diff --git a/gfx/gl/bufferedLocation.cpp b/gfx/gl/bufferedLocation.cpp
deleted file mode 100644
index f1bedfe..0000000
--- a/gfx/gl/bufferedLocation.cpp
+++ /dev/null
@@ -1,70 +0,0 @@
-#include "bufferedLocation.h"
-#include "location.h"
-#include <glm/gtx/transform.hpp>
-
-BufferedLocation::BufferedLocation(GlobalPosition3D p, Rotation3D r) : BufferedLocation {Location {p, r}} { }
-
-BufferedLocation::BufferedLocation(const Location & l) : loc {l} { }
-
-BufferedLocation::operator const Location &() const
-{
- return loc;
-}
-
-BufferedLocation &
-BufferedLocation::operator=(const Location & l)
-{
- loc = l;
- updateBuffer();
- return *this;
-}
-
-GlobalPosition3D
-BufferedLocation::position() const
-{
- return loc.pos;
-}
-
-Rotation3D
-BufferedLocation::rotation() const
-{
- return loc.rot;
-}
-
-void
-BufferedLocation::setPosition(GlobalPosition3D p, bool update)
-{
- loc.pos = p;
- if (update) {
- updateBuffer();
- }
-}
-
-void
-BufferedLocation::setRotation(Rotation3D r, bool update)
-{
- loc.rot = r;
- if (update) {
- updateBuffer();
- }
-}
-
-void
-BufferedLocation::setLocation(GlobalPosition3D p, Rotation3D r)
-{
- loc.pos = p;
- loc.rot = r;
- updateBuffer();
-}
-
-glm::mat4
-BufferedLocation::getRotationTransform() const
-{
- return loc.getRotationTransform();
-}
-
-void
-BufferedLocationUpdater::updateBuffer() const
-{
- onUpdate(this);
-}
diff --git a/gfx/gl/bufferedLocation.h b/gfx/gl/bufferedLocation.h
deleted file mode 100644
index 87b957f..0000000
--- a/gfx/gl/bufferedLocation.h
+++ /dev/null
@@ -1,48 +0,0 @@
-#pragma once
-
-#include "location.h"
-#include <functional>
-#include <glm/mat4x4.hpp>
-#include <glm/vec3.hpp>
-#include <utility>
-
-class BufferedLocation {
-public:
- BufferedLocation(GlobalPosition3D = {}, Rotation3D = {});
- BufferedLocation(const Location &);
- virtual ~BufferedLocation() = default;
-
- BufferedLocation & operator=(const Location &);
-
- operator const Location &() const;
-
- [[nodiscard]] GlobalPosition3D position() const;
- [[nodiscard]] Rotation3D rotation() const;
- void setPosition(GlobalPosition3D, bool update = true);
- void setRotation(Rotation3D, bool update = true);
- void setLocation(GlobalPosition3D, Rotation3D);
-
- [[nodiscard]] glm::mat4 getRotationTransform() const;
-
-private:
- virtual void updateBuffer() const = 0;
-
- Location loc;
-};
-
-class BufferedLocationUpdater : public BufferedLocation {
-public:
- template<typename... LocationArgs>
- BufferedLocationUpdater(std::function<void(const BufferedLocation *)> onUpdate, LocationArgs &&... t) :
- BufferedLocation {std::forward<LocationArgs>(t)...}, onUpdate {std::move(onUpdate)}
- {
- updateBuffer();
- }
-
- using BufferedLocation::operator=;
-
-private:
- void updateBuffer() const override;
-
- std::function<void(const BufferedLocation *)> onUpdate;
-};
diff --git a/gfx/gl/glBuffer.h b/gfx/gl/glBuffer.h
new file mode 100644
index 0000000..275276f
--- /dev/null
+++ b/gfx/gl/glBuffer.h
@@ -0,0 +1,24 @@
+#pragma once
+
+#include "glArrays.h"
+
+namespace Impl {
+ struct glBuffer : Detail::glNamed {
+ void
+ storage(const std::ranges::contiguous_range auto & data, GLenum flags)
+ {
+ glNamedBufferStorage(
+ name, static_cast<GLsizeiptr>(data.size() * sizeof(decltype(*data.data()))), data.data(), flags);
+ }
+
+ void
+ data(const std::ranges::contiguous_range auto & data, GLenum flags)
+ {
+ glNamedBufferData(
+ name, static_cast<GLsizeiptr>(data.size() * sizeof(decltype(*data.data()))), data.data(), flags);
+ }
+ };
+}
+
+template<size_t N> using glBuffers = glManagedArray<Impl::glBuffer, N, &glCreateBuffers, &glDeleteBuffers>;
+using glBuffer = glManagedSingle<Impl::glBuffer, &glCreateBuffers, &glDeleteBuffers>;
diff --git a/gfx/gl/glFramebuffer.cpp b/gfx/gl/glFramebuffer.cpp
new file mode 100644
index 0000000..fb3290f
--- /dev/null
+++ b/gfx/gl/glFramebuffer.cpp
@@ -0,0 +1,28 @@
+#include "glFramebuffer.h"
+#include <stdexcept>
+
+void
+Impl::glRenderbuffer::storage(const GLenum iformat, const ImageDimensions dims)
+{
+ glNamedRenderbufferStorage(name, iformat, dims.x, dims.y);
+}
+
+void
+Impl::glFramebuffer::buffer(const GLenum attachment, const Impl::glRenderbuffer & buffer)
+{
+ glNamedFramebufferRenderbuffer(name, attachment, GL_RENDERBUFFER, buffer);
+}
+
+void
+Impl::glFramebuffer::drawBuffers(const std::span<const GLenum> buffers)
+{
+ glNamedFramebufferDrawBuffers(name, static_cast<GLsizei>(buffers.size()), buffers.data());
+}
+
+void
+Impl::glFramebuffer::assertComplete() const
+{
+ if (glCheckNamedFramebufferStatus(name, GL_DRAW_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
+ throw std::runtime_error("Framebuffer not complete!");
+ }
+}
diff --git a/gfx/gl/glFramebuffer.h b/gfx/gl/glFramebuffer.h
new file mode 100644
index 0000000..0a4ec5a
--- /dev/null
+++ b/gfx/gl/glFramebuffer.h
@@ -0,0 +1,40 @@
+#pragma once
+
+#include "config/types.h"
+#include "glArrays.h"
+#include "glTexture.h"
+
+namespace Impl {
+ struct glRenderbuffer : Detail::glNamed {
+ void storage(GLenum iformat, ImageDimensions);
+ };
+
+ struct glFramebuffer : Detail::glNamed {
+ template<GLenum Target>
+ void
+ texture(GLenum attachment, const glTexture<Target> & texture)
+ {
+ glNamedFramebufferTexture(name, attachment, texture, 0);
+ }
+
+ void drawBuffers(std::span<const GLenum> buffers);
+
+ template<std::convertible_to<GLenum>... Buffers>
+ void
+ drawBuffers(Buffers... buffers)
+ {
+ drawBuffers(std::array {static_cast<GLenum>(buffers)...});
+ }
+
+ void buffer(GLenum attachment, const glRenderbuffer &);
+ void assertComplete() const;
+ };
+}
+
+template<size_t N>
+using glFramebuffers = glManagedArray<Impl::glFramebuffer, N, &glCreateFramebuffers, &glDeleteFramebuffers>;
+using glFramebuffer = glManagedSingle<Impl::glFramebuffer, &glCreateFramebuffers, &glDeleteFramebuffers>;
+
+template<size_t N>
+using glRenderbuffers = glManagedArray<Impl::glRenderbuffer, N, &glCreateRenderbuffers, &glDeleteRenderbuffers>;
+using glRenderbuffer = glManagedSingle<Impl::glRenderbuffer, &glCreateRenderbuffers, &glDeleteRenderbuffers>;
diff --git a/gfx/gl/glTexture.cpp b/gfx/gl/glTexture.cpp
new file mode 100644
index 0000000..51a27ed
--- /dev/null
+++ b/gfx/gl/glTexture.cpp
@@ -0,0 +1,195 @@
+#include "glTexture.h"
+#include "config/types.h"
+#include "filesystem.h"
+#include "gfx/models/tga.h"
+#include "maths.h"
+#include <fcntl.h>
+#include <ranges>
+#include <sys/mman.h>
+
+void
+Impl::glTextureBase::bind(const GLuint unit) const
+{
+ glBindTextureUnit(unit, name);
+}
+
+void
+Impl::glTextureBase::generateMipmap()
+{
+ glGenerateTextureMipmap(name);
+}
+
+template<glm::length_t Dims>
+ requires(Dims >= 1 && Dims <= 3)
+glm::vec<Dims, GLsizei>
+Impl::glTextureDims<Dims>::getSize() const
+{
+ static constexpr std::array<GLenum, 3> PARAMS {GL_TEXTURE_WIDTH, GL_TEXTURE_HEIGHT, GL_TEXTURE_DEPTH};
+ glm::vec<Dims, GLsizei> size {};
+ for (auto [dim, param] : std::views::enumerate(PARAMS) | std::views::take(Dims)) {
+ glGetTextureLevelParameteriv(name, 0, param, &size[static_cast<glm::length_t>(dim)]);
+ }
+ return size;
+}
+
+template<>
+void
+Impl::glTextureDims<1>::storage(const GLsizei levels, const GLenum internalformat, glm::vec<1, GLsizei> dims)
+{
+ glTextureStorage1D(name, levels, internalformat, dims.x);
+}
+
+template<>
+void
+Impl::glTextureDims<2>::storage(const GLsizei levels, const GLenum internalformat, glm::vec<2, GLsizei> dims)
+{
+ glTextureStorage2D(name, levels, internalformat, dims.x, dims.y);
+}
+
+template<>
+void
+Impl::glTextureDims<3>::storage(const GLsizei levels, const GLenum internalformat, glm::vec<3, GLsizei> dims)
+{
+ glTextureStorage3D(name, levels, internalformat, dims.x, dims.y, dims.z);
+}
+
+template<>
+void
+Impl::glTextureDims<1>::subImage(
+ glm::vec<1, GLint> offset, glm::vec<1, GLint> size, const GLenum format, const GLenum type, const void * pixels)
+{
+ glTextureSubImage1D(name, 0, offset.x, size.x, format, type, pixels);
+}
+
+template<>
+void
+Impl::glTextureDims<2>::subImage(
+ glm::vec<2, GLint> offset, glm::vec<2, GLint> size, const GLenum format, const GLenum type, const void * pixels)
+{
+ glTextureSubImage2D(name, 0, offset.x, offset.y, size.x, size.y, format, type, pixels);
+}
+
+template<>
+void
+Impl::glTextureDims<3>::subImage(
+ glm::vec<3, GLint> offset, glm::vec<3, GLint> size, const GLenum format, const GLenum type, const void * pixels)
+{
+ glTextureSubImage3D(name, 0, offset.x, offset.y, offset.z, size.x, size.y, size.z, format, type, pixels);
+}
+
+template<glm::length_t Dims>
+ requires(Dims >= 1 && Dims <= 3)
+void
+Impl::glTextureDims<Dims>::image(
+ glm::vec<Dims, GLint> size, const GLenum format, const GLenum type, const void * pixels)
+{
+ subImage({}, size, format, type, pixels);
+}
+
+template<glm::length_t Dims>
+ requires(Dims >= 1 && Dims <= 3)
+void
+Impl::glTextureDims<Dims>::image(const GLenum format, const GLenum type, const void * pixels)
+{
+ image(getSize(), format, type, pixels);
+}
+
+namespace {
+ template<glm::length_t Dims>
+ requires(Dims >= 1 && Dims <= 3)
+ size_t
+ areaOf(glm::vec<Dims, GLsizei> size)
+ {
+ size_t area = 1;
+ for (auto dim = 0; dim < Dims; ++dim) {
+ area *= static_cast<size_t>(size[dim]);
+ }
+ return area;
+ }
+
+ template<glm::length_t Dims, glm::length_t channels>
+ requires(Dims >= 1 && Dims <= 3)
+ void
+ save(const Impl::glTextureDims<Dims> & texture, const GLenum format, const GLenum type, const char * path,
+ uint8_t tgaFormat)
+ {
+ const auto size = texture.getSize();
+ const auto area = areaOf(size);
+ size_t dataSize = area * channels;
+ const size_t fileSize = dataSize + sizeof(TGAHead<channels>);
+
+ filesystem::fh out {path, O_RDWR | O_CREAT, 0660};
+ out.truncate(fileSize);
+ auto tga = out.mmap(fileSize, 0, PROT_WRITE, MAP_SHARED);
+ auto outTga = tga.get<TGAHead<channels>>();
+ *outTga = {
+ .format = tgaFormat,
+ .size = {size.x, (area / static_cast<size_t>(size.x))},
+ };
+ glPixelStorei(GL_PACK_ALIGNMENT, 1);
+ glGetTextureImage(texture, 0, format, type, static_cast<GLsizei>(dataSize), outTga->data);
+ tga.msync(MS_ASYNC);
+ }
+}
+
+template<glm::length_t Dims>
+ requires(Dims >= 1 && Dims <= 3)
+void
+Impl::glTextureDims<Dims>::saveColour(const char * path) const
+{
+ save<Dims, 3>(*this, GL_BGR, GL_UNSIGNED_BYTE, path, 2);
+}
+
+template<glm::length_t Dims>
+ requires(Dims >= 1 && Dims <= 3)
+void
+Impl::glTextureDims<Dims>::savePosition(const char * path) const
+{
+ const auto size = getSize();
+ const auto area = areaOf(size);
+ size_t dataSize = area * sizeof(TGAHead<3>::PixelType);
+ const size_t fileSize = dataSize + sizeof(TGAHead<3>);
+
+ filesystem::fh out {path, O_RDWR | O_CREAT, 0660};
+ out.truncate(fileSize);
+ auto tga = out.mmap(fileSize, 0, PROT_WRITE, MAP_SHARED);
+ auto outTga = tga.get<TGAHead<3>>();
+ *outTga = {
+ .format = 2,
+ .size = {size.x, (area / static_cast<size_t>(size.x))},
+ };
+ glPixelStorei(GL_PACK_ALIGNMENT, 1);
+ std::vector<RelativePosition3D> raw {area};
+ glGetTextureImage(name, 0, GL_BGR, GL_FLOAT, static_cast<GLsizei>(sizeof(GlobalPosition3D) * area), raw.data());
+ using Comp = RelativePosition3D (*)(const RelativePosition3D &, const RelativePosition3D &);
+ auto notZero = std::views::filter([](const RelativePosition3D & pos) {
+ return pos != RelativePosition3D {};
+ });
+ const auto minPos = *std::ranges::fold_left_first(raw | notZero, static_cast<Comp>(&glm::min));
+ const auto maxPos = *std::ranges::fold_left_first(raw | notZero, static_cast<Comp>(&glm::max));
+ const auto rangePos = difference(maxPos, minPos);
+ std::ranges::transform(raw, outTga->data, [minPos, rangePos](const auto & pos) {
+ return 255.F * ((pos - minPos) / rangePos);
+ });
+ tga.msync(MS_ASYNC);
+}
+
+template<glm::length_t Dims>
+ requires(Dims >= 1 && Dims <= 3)
+void
+Impl::glTextureDims<Dims>::saveDepth(const char * path) const
+{
+ save<Dims, 1>(*this, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, path, 3);
+}
+
+template<glm::length_t Dims>
+ requires(Dims >= 1 && Dims <= 3)
+void
+Impl::glTextureDims<Dims>::saveNormal(const char * path) const
+{
+ save<Dims, 3>(*this, GL_BGR, GL_BYTE, path, 2);
+}
+
+template struct Impl::glTextureDims<1>;
+template struct Impl::glTextureDims<2>;
+template struct Impl::glTextureDims<3>;
diff --git a/gfx/gl/glTexture.h b/gfx/gl/glTexture.h
new file mode 100644
index 0000000..3472c4a
--- /dev/null
+++ b/gfx/gl/glTexture.h
@@ -0,0 +1,72 @@
+#pragma once
+
+#include "glArrays.h"
+#include "gl_traits.h"
+
+namespace Impl {
+ template<GLenum> struct TextureTargetTraits;
+
+ template<GLenum Target> struct TextureTargetTraitsCommon {
+ static void
+ create(GLsizei count, GLuint * textures)
+ {
+ glCreateTextures(Target, count, textures);
+ }
+ };
+
+ template<> struct TextureTargetTraits<GL_TEXTURE_2D> : TextureTargetTraitsCommon<GL_TEXTURE_2D> {
+ constexpr static glm::length_t dims = 2;
+ };
+
+ template<> struct TextureTargetTraits<GL_TEXTURE_2D_ARRAY> : TextureTargetTraitsCommon<GL_TEXTURE_2D_ARRAY> {
+ constexpr static glm::length_t dims = 3;
+ };
+
+ template<> struct TextureTargetTraits<GL_TEXTURE_RECTANGLE> : TextureTargetTraitsCommon<GL_TEXTURE_RECTANGLE> {
+ constexpr static glm::length_t dims = 2;
+ };
+
+ struct glTextureBase : Detail::glNamed {
+ void bind(GLuint unit) const;
+ void generateMipmap();
+
+ template<has_glTextureParameter T>
+ void
+ parameter(GLenum pname, T param)
+ {
+ (*gl_traits<T>::glTextureParameterFunc)(name, pname, param);
+ }
+
+ template<glm::length_t L, has_glTextureParameterv T, glm::qualifier Q>
+ void
+ parameter(GLenum pname, const glm::vec<L, T, Q> & param)
+ {
+ (*gl_traits<T>::glTextureParametervFunc)(name, pname, glm::value_ptr(param));
+ }
+ };
+
+ template<glm::length_t Dims>
+ requires(Dims >= 1 && Dims <= 3)
+ struct glTextureDims : glTextureBase {
+ [[nodiscard]] glm::vec<Dims, GLsizei> getSize() const;
+ void storage(GLsizei levels, GLenum internalformat, glm::vec<Dims, GLsizei> dims);
+ void image(GLenum format, GLenum type, const void * pixels);
+ void image(glm::vec<Dims, GLint> size, GLenum format, GLenum type, const void * pixels);
+ void subImage(glm::vec<Dims, GLint> offset, glm::vec<Dims, GLint> size, GLenum format, GLenum type,
+ const void * pixels);
+
+ void saveColour(const char * path) const;
+ void saveDepth(const char * path) const;
+ void saveNormal(const char * path) const;
+ void savePosition(const char * path) const;
+ };
+
+ template<GLenum Target> struct glTexture : glTextureDims<TextureTargetTraits<Target>::dims> { };
+}
+
+template<GLenum Target, size_t N>
+using glTextures
+ = glManagedArray<Impl::glTexture<Target>, N, &Impl::TextureTargetTraits<Target>::create, &glDeleteTextures>;
+template<GLenum Target>
+using glTexture
+ = glManagedSingle<Impl::glTexture<Target>, &Impl::TextureTargetTraits<Target>::create, &glDeleteTextures>;
diff --git a/gfx/gl/glVertexArray.h b/gfx/gl/glVertexArray.h
new file mode 100644
index 0000000..4e8113f
--- /dev/null
+++ b/gfx/gl/glVertexArray.h
@@ -0,0 +1,162 @@
+#pragma once
+
+#include "collections.h"
+#include "glArrays.h"
+#include "glBuffer.h"
+#include "gl_traits.h"
+#include "util.h"
+
+namespace Impl {
+ class VertexArrayConfigurator {
+ public:
+ template<typename M, typename T> struct MP {
+ constexpr MP(M T::* ptr) : ptr {ptr} { }
+
+ constexpr
+ operator GLuint() const
+ {
+ constexpr static char dummy {};
+ return static_cast<GLuint>(reinterpret_cast<const char *>(&(reinterpret_cast<const T *>(&dummy)->*ptr))
+ - reinterpret_cast<const char *>(&dummy));
+ }
+
+ M T::* ptr;
+ };
+
+ explicit VertexArrayConfigurator(GLuint name) : name {name} { }
+
+ VertexArrayConfigurator &
+ addIndices(const glBuffer & buffer)
+ {
+ glVertexArrayElementBuffer(name, buffer);
+ return *this;
+ }
+
+ VertexArrayConfigurator &
+ addIndices(glBuffer & buffer, const SequentialCollection<GLuint> auto & indices)
+ {
+ buffer.storage(indices, 0);
+ return addIndices(buffer);
+ }
+
+ // Customisation point
+ template<typename VertexT> VertexArrayConfigurator & addAttribsFor(GLuint divisor);
+
+ template<typename VertexT>
+ VertexArrayConfigurator &
+ addAttribsFor(GLuint divisor, const glBuffer & buffer)
+ {
+ glVertexArrayVertexBuffer(name, binding, buffer, 0, sizeof(VertexT));
+ return addAttribsFor<VertexT>(divisor);
+ }
+
+ template<typename VertexT, auto... Attribs>
+ VertexArrayConfigurator &
+ addAttribs(const GLuint divisor)
+ {
+ configureAttribs<VertexT, Attribs...>(divisor);
+ return *this;
+ }
+
+ template<typename VertexT, auto... Attribs>
+ VertexArrayConfigurator &
+ addAttribs(const GLuint divisor, const glBuffer & buffer)
+ {
+ glVertexArrayVertexBuffer(name, binding, buffer, 0, sizeof(VertexT));
+ return addAttribs<VertexT, Attribs...>(divisor);
+ }
+
+ template<typename VertexT, auto... Attribs>
+ VertexArrayConfigurator &
+ addAttribs(const GLuint divisor, glBuffer & buffer, const SequentialCollection<VertexT> auto & data)
+ {
+ buffer.storage(data, 0);
+ return addAttribs<VertexT, Attribs...>(divisor, buffer);
+ }
+
+ private:
+ void
+ setPointerMeta(const GLuint usedAttribs)
+ {
+ while (attrib < usedAttribs) {
+ glEnableVertexArrayAttrib(name, attrib);
+ glVertexArrayAttribBinding(name, attrib++, binding);
+ }
+ }
+
+ template<typename T>
+ void
+ setPointer(const GLuint offset)
+ {
+ setPointerMeta(attrib + gl_traits<T>::vertexArrayAttribFormat(name, attrib, offset));
+ }
+
+ template<typename VertexT, auto Attrib>
+ void
+ setPointer()
+ {
+ using Mbr = MemberValueType<Attrib>;
+ setPointer<Mbr>(MP<Mbr, VertexT> {Attrib});
+ }
+
+ template<typename VertexT, auto... Attribs>
+ void
+ configureAttribs(const GLuint divisor)
+ {
+ if constexpr (sizeof...(Attribs) == 0) {
+ setPointer<VertexT>(0);
+ }
+ else {
+ ((setPointer<VertexT, Attribs>()), ...);
+ }
+ glVertexArrayBindingDivisor(name, binding++, divisor);
+ }
+
+ GLuint name;
+ GLuint binding = 0;
+ GLuint attrib = 0;
+ };
+
+ struct glVertexArray : Detail::glNamed {
+ VertexArrayConfigurator
+ configure()
+ {
+ return VertexArrayConfigurator {name};
+ }
+
+ template<typename glAllocated>
+ void
+ useBuffer(GLuint binding, const glAllocated & buffer, GLsizei offset = 0) const
+ requires requires {
+ { buffer.bufferName() } -> std::same_as<GLuint>;
+ }
+ {
+ using T = typename glAllocated::value_type;
+ useBuffer(binding, buffer.bufferName(), sizeof(T), offset);
+ }
+
+ template<typename V>
+ void
+ useBuffer(GLuint binding, GLuint bufferName, auto V::* mbr) const
+ {
+ useBuffer(binding, bufferName, sizeof(V), VertexArrayConfigurator::MP {mbr});
+ }
+
+ template<typename V>
+ void
+ useBuffer(GLuint binding, GLuint bufferName, GLintptr offset = 0) const
+ {
+ useBuffer(binding, bufferName, sizeof(V), offset);
+ }
+
+ void
+ useBuffer(GLuint binding, GLuint bufferName, GLsizei stride, GLintptr offset = 0) const
+ {
+ glVertexArrayVertexBuffer(name, binding, bufferName, offset, stride);
+ }
+ };
+}
+
+template<size_t N>
+using glVertexArrays = glManagedArray<Impl::glVertexArray, N, &glCreateVertexArrays, &glDeleteVertexArrays>;
+using glVertexArray = glManagedSingle<Impl::glVertexArray, &glCreateVertexArrays, &glDeleteVertexArrays>;
diff --git a/gfx/gl/gldebug.cpp b/gfx/gl/gldebug.cpp
new file mode 100644
index 0000000..518d4fb
--- /dev/null
+++ b/gfx/gl/gldebug.cpp
@@ -0,0 +1,20 @@
+#if GLDEBUG == 2
+// Level 2 is out of line because its "complex"
+
+# include "gldebug.h"
+# include <format>
+
+glDebugScope::glDebugScope(GLuint id, const std::source_location & location)
+{
+ const auto fullMsg = std::format("{} ({}:{})", location.function_name(), location.file_name(), location.line());
+ glPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, id, static_cast<GLsizei>(fullMsg.length()), fullMsg.c_str());
+}
+
+glDebugScope::glDebugScope(GLuint id, const std::string_view msg, const std::source_location & location)
+{
+ const auto fullMsg
+ = std::format("{} @ {} ({}:{})", msg, location.function_name(), location.file_name(), location.line());
+ glPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, id, static_cast<GLsizei>(fullMsg.length()), fullMsg.c_str());
+}
+
+#endif
diff --git a/gfx/gl/gldebug.h b/gfx/gl/gldebug.h
new file mode 100644
index 0000000..5cfd099
--- /dev/null
+++ b/gfx/gl/gldebug.h
@@ -0,0 +1,52 @@
+#pragma once
+#ifndef GLDEBUG
+# define GLDEBUG 0
+#endif
+
+#include "special_members.h"
+#include <glad/gl.h>
+#include <source_location>
+#include <string_view>
+
+class [[nodiscard]] glDebugScope {
+public:
+ explicit glDebugScope(GLuint id, const std::source_location & = std::source_location::current());
+ explicit glDebugScope(
+ GLuint id, std::string_view msg, const std::source_location & = std::source_location::current());
+
+ ~glDebugScope();
+
+ constexpr
+ operator bool() const
+ {
+ return true;
+ }
+
+ NO_MOVE(glDebugScope);
+ NO_COPY(glDebugScope);
+};
+
+#if GLDEBUG > 0
+inline glDebugScope::~glDebugScope()
+{
+ glPopDebugGroup();
+}
+# if GLDEBUG == 1
+// Level 1 is inlined for performance because they're thin wrappers
+inline glDebugScope::glDebugScope(GLuint id, const std::source_location & location) :
+ glDebugScope {id, location.function_name()}
+{
+}
+
+inline glDebugScope::glDebugScope(GLuint id, const std::string_view msg, const std::source_location &)
+{
+ glPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, id, static_cast<GLsizei>(msg.length()), msg.data());
+}
+# endif
+#else
+inline glDebugScope::glDebugScope(GLuint, const std::source_location &) { }
+
+inline glDebugScope::glDebugScope(GLuint, const std::string_view, const std::source_location &) { }
+
+inline glDebugScope::~glDebugScope() = default;
+#endif
diff --git a/gfx/gl/instanceVertices.h b/gfx/gl/instanceVertices.h
index 28e11ee..9963a96 100644
--- a/gfx/gl/instanceVertices.h
+++ b/gfx/gl/instanceVertices.h
@@ -1,17 +1,20 @@
#pragma once
-#include "glContainer.h"
+#include "glAllocator.h"
+#include <algorithm>
#include <cassert>
+#include <functional>
#include <special_members.h>
#include <utility>
-template<typename T> class InstanceVertices : protected glContainer<T> {
- using base = glContainer<T>;
+template<typename T> class InstanceVertices : protected glVector<T> {
+ using base = glVector<T>;
+ using IndexT = uint32_t;
public:
class [[nodiscard]] InstanceProxy {
public:
- InstanceProxy(InstanceVertices * iv, std::size_t idx) : instances {iv}, index {idx} { }
+ InstanceProxy(InstanceVertices * iv, IndexT idx) : instances {iv}, index {idx} { }
InstanceProxy(InstanceProxy && other) noexcept :
instances {std::exchange(other.instances, nullptr)}, index {other.index}
@@ -47,13 +50,15 @@ public:
}
// NOLINTNEXTLINE)hicpp-explicit-conversions
- [[nodiscard]] operator T &()
+ [[nodiscard]]
+ operator T &()
{
return instances->lookup(index);
}
// NOLINTNEXTLINE)hicpp-explicit-conversions
- [[nodiscard]] operator const T &() const
+ [[nodiscard]]
+ operator const T &() const
{
return instances->lookup(index);
}
@@ -94,9 +99,9 @@ public:
return instances->lookup(index);
}
- private:
+ // private:
InstanceVertices<T> * instances;
- std::size_t index;
+ IndexT index;
};
template<typename... Params>
@@ -106,41 +111,91 @@ public:
if (!unused.empty()) {
auto idx = unused.back();
unused.pop_back();
- index[idx] = base::size();
+ index[idx] = static_cast<IndexT>(base::size());
reverseIndex.emplace_back(idx);
base::emplace_back(std::forward<Params>(params)...);
return InstanceProxy {this, idx};
}
- index.emplace_back(base::size());
- reverseIndex.push_back(base::size());
+ index.emplace_back(static_cast<IndexT>(base::size()));
+ reverseIndex.push_back(static_cast<IndexT>(base::size()));
base::emplace_back(std::forward<Params>(params)...);
- return InstanceProxy {this, index.size() - 1};
+ return InstanceProxy {this, static_cast<IndexT>(index.size() - 1)};
}
- using base::bufferName;
+ [[nodiscard]] GLuint
+ bufferName() const
+ {
+ return base::begin().base().bufferName();
+ }
- [[nodiscard]] auto
- size() const
+ [[nodiscard]] GLuint
+ indexBufferName() const
{
- base::unmap();
- return base::size();
+ return index.begin().base().bufferName();
}
+ using typename base::value_type;
+
+ using base::at;
+ using base::begin;
+ using base::cbegin;
+ using base::cend;
+ using base::crbegin;
+ using base::crend;
+ using base::end;
+ using base::rbegin;
+ using base::rend;
+ using base::size;
+ using base::operator[];
+ using base::back;
+ using base::capacity;
+ using base::data;
+ using base::empty;
+ using base::front;
+ using base::reserve;
+ using base::shrink_to_fit;
+
template<typename Pred>
- glContainer<T>::iterator
+ base::size_type
partition(Pred pred)
{
- return partition(base::begin(), base::end(), pred);
+ return indexOf(partition(base::begin(), base::end(), pred));
+ }
+
+ using PartitionResult
+ = std::pair<typename base::size_type, std::pair<typename base::size_type, typename base::size_type>>;
+
+ template<typename Pred1, typename Pred2>
+ PartitionResult
+ partition(Pred1 pred1, Pred2 pred2)
+ {
+ return partition(pred1, std::not_fn(pred2), pred2);
+ }
+
+ template<typename Pred1, typename Pred2, typename Pred3>
+ PartitionResult
+ partition(Pred1 pred1, Pred2 pred2, Pred3 pred3)
+ {
+ auto boundary1 = partition(base::begin(), base::end(), pred1);
+ auto begin2 = partition(base::begin(), boundary1, pred2);
+ auto end2 = partition(boundary1, base::end(), pred3);
+ return {indexOf(boundary1), {indexOf(begin2), indexOf(end2)}};
}
protected:
- static constexpr auto npos = static_cast<size_t>(-1);
+ static constexpr auto npos = static_cast<IndexT>(-1);
friend InstanceProxy;
+ base::size_type
+ indexOf(base::iterator iter)
+ {
+ return static_cast<base::size_type>(iter - base::begin());
+ }
+
void
- release(const size_t pidx)
+ release(const IndexT pidx)
{
- if (const size_t last = base::size() - 1; last != index[pidx]) {
+ if (const auto last = static_cast<IndexT>(base::size() - 1); last != index[pidx]) {
lookup(pidx) = std::move(base::back());
const auto movedKey = reverseIndex[last];
index[movedKey] = std::exchange(index[pidx], npos);
@@ -159,14 +214,14 @@ protected:
}
[[nodiscard]] T &
- lookup(size_t pindex)
+ lookup(IndexT pindex)
{
return base::data()[index[pindex]];
}
template<typename Pred>
- glContainer<T>::iterator
- partition(glContainer<T>::iterator first, glContainer<T>::iterator last, Pred pred)
+ base::iterator
+ partition(base::iterator first, base::iterator last, Pred pred)
{
while (first < last) {
first = std::find_if_not(first, last, pred);
@@ -183,8 +238,8 @@ protected:
}
// Index into buffer given to nth proxy
- std::vector<size_t> index;
- std::vector<size_t> reverseIndex;
+ glVector<IndexT> index;
+ std::vector<IndexT> reverseIndex;
// List of free spaces in index
- std::vector<size_t> unused;
+ std::vector<IndexT> unused;
};
diff --git a/gfx/gl/program.cpp b/gfx/gl/program.cpp
index fdd4c6f..da85456 100644
--- a/gfx/gl/program.cpp
+++ b/gfx/gl/program.cpp
@@ -1,5 +1,5 @@
#include "program.h"
-#include "shader.h"
+#include "gldebug.h"
#include <format>
#include <glm/gtc/type_ptr.hpp>
#include <glm/gtx/transform.hpp>
@@ -9,6 +9,7 @@
void
Program::linkAndValidate() const
{
+ glDebugScope _ {m_program};
glLinkProgram(m_program);
checkProgramError(m_program, GL_LINK_STATUS, "Error linking shader program");
diff --git a/gfx/gl/program.h b/gfx/gl/program.h
index c89a128..2b06d2d 100644
--- a/gfx/gl/program.h
+++ b/gfx/gl/program.h
@@ -1,5 +1,6 @@
#pragma once
+#include "gldebug.h"
#include "shader.h" // IWYU pragma: export
#include <glRef.h>
#include <glad/gl.h>
@@ -14,7 +15,9 @@ class Program {
public:
Program() = delete;
- template<typename... S> explicit Program(const S &... srcs)
+ template<typename... S> explicit Program(const S &... srcs) : Program {glDebugScope {0}, srcs...} { }
+
+ template<typename... S> explicit Program(glDebugScope, const S &... srcs)
{
(glAttachShader(m_program, srcs.compile()), ...);
linkAndValidate();
@@ -33,6 +36,12 @@ public:
return location;
}
+ explicit
+ operator bool() const
+ {
+ return location >= 0;
+ }
+
protected:
GLint location;
};
diff --git a/gfx/gl/sceneProvider.cpp b/gfx/gl/sceneProvider.cpp
index 2e8604c..3681c60 100644
--- a/gfx/gl/sceneProvider.cpp
+++ b/gfx/gl/sceneProvider.cpp
@@ -5,10 +5,10 @@ void
SceneProvider::environment(const SceneShader &, const SceneRenderer & renderer) const
{
renderer.setAmbientLight({0.5F, 0.5F, 0.5F});
- renderer.setDirectionalLight({0.6F, 0.6F, 0.6F}, {-1, 1, -1}, *this);
+ renderer.setDirectionalLight({0.6F, 0.6F, 0.6F}, {{-quarter_pi, quarter_pi}}, *this);
}
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 f5e8e99..e69885a 100644
--- a/gfx/gl/sceneProvider.h
+++ b/gfx/gl/sceneProvider.h
@@ -1,10 +1,13 @@
#pragma once
+#include <functional>
#include <special_members.h>
class SceneRenderer;
class ShadowMapper;
class SceneShader;
+class Frustum;
+class Renderable;
class SceneProvider {
public:
@@ -12,8 +15,10 @@ public:
virtual ~SceneProvider() = default;
DEFAULT_MOVE_COPY(SceneProvider);
- virtual void content(const SceneShader &) const = 0;
+ using RenderableProcessor = std::function<void(Renderable *)>;
+ virtual void forEachRenderable(const RenderableProcessor &) 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;
+ virtual void shadows(const ShadowMapper &, const Frustum &) const;
};
diff --git a/gfx/gl/sceneRenderer.cpp b/gfx/gl/sceneRenderer.cpp
index e0938f2..a809ed5 100644
--- a/gfx/gl/sceneRenderer.cpp
+++ b/gfx/gl/sceneRenderer.cpp
@@ -1,9 +1,10 @@
#include "sceneRenderer.h"
#include "maths.h"
-#include "vertexArrayObject.h"
-#include <gfx/gl/shaders/fs-directionalLight.h>
-#include <gfx/gl/shaders/fs-lighting.h>
-#include <gfx/gl/shaders/vs-lighting.h>
+#include "stream_support.h"
+#include <gfx/gl/shaders/directionalLight-frag.h>
+#include <gfx/gl/shaders/lighting-frag.h>
+#include <gfx/gl/shaders/lighting-vert.h>
+#include <gfx/renderable.h>
#include <glm/gtc/type_ptr.hpp>
static constexpr const std::array<const glm::i8vec4, 4> displayVAOdata {{
@@ -14,109 +15,154 @@ static constexpr const std::array<const glm::i8vec4, 4> displayVAOdata {{
{1, -1, 1, 0},
}};
-SceneRenderer::SceneRenderer(ScreenAbsCoord s, GLuint o) :
+SceneRenderer::SceneRenderer(ScreenAbsCoord s, GLuint o) : SceneRenderer {s, o, glDebugScope {o}} { }
+
+SceneRenderer::SceneRenderer(ScreenAbsCoord s, GLuint o, glDebugScope) :
camera {{-1250000, -1250000, 35.0F}, quarter_pi, ratio(s), 100, 10000000}, size {s}, output {o},
- lighting {lighting_vs, lighting_fs}, shadowMapper {{2048, 2048}}
+ lighting {lighting_vert, lighting_frag}, shadowMapper {{2048, 2048}}
{
shader.setViewPort({0, 0, size.x, size.y});
- VertexArrayObject {displayVAO}.addAttribs<glm::i8vec4>(displayVBO, displayVAOdata);
+ displayVAO.configure().addAttribs<glm::i8vec4>(0, displayVBO, displayVAOdata);
- const auto configuregdata = [this](const GLuint data, const std::initializer_list<GLint> iformats,
- const GLenum format, const GLenum attachment) {
- glBindTexture(GL_TEXTURE_2D, data);
- glTexParameter(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
- glTexParameter(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
- for (const auto iformat : iformats) {
- glTexImage2D(GL_TEXTURE_2D, 0, iformat, size.x, size.y, 0, format, GL_BYTE, nullptr);
+ gBuffer.drawBuffers(GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2);
+ gBufferIll.drawBuffers(GL_COLOR_ATTACHMENT0);
+ configureBuffers();
+}
- glFramebufferTexture2D(GL_FRAMEBUFFER, attachment, GL_TEXTURE_2D, data, 0);
- if (glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE) {
- return iformat;
- }
- }
- throw std::runtime_error("Framebuffer could not be completed!");
+void
+SceneRenderer::resize(ScreenAbsCoord newSize)
+{
+ glDebugScope _ {output};
+ size = newSize;
+ shader.setViewPort({0, 0, size.x, size.y});
+ camera.setAspect(ratio(size));
+
+ depth = {};
+ gPosition = {};
+ gNormal = {};
+ gAlbedoSpec = {};
+ gIllumination = {};
+ configureBuffers();
+}
+
+void
+SceneRenderer::configureBuffers()
+{
+ const auto configureAttachment = [this](glFramebuffer & fbo, glTexture<GL_TEXTURE_2D> & data, const GLenum iformat,
+ const GLenum attachment) {
+ data.storage(1, iformat, size);
+ data.parameter(GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ data.parameter(GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ fbo.texture(attachment, data);
};
- glBindFramebuffer(GL_FRAMEBUFFER, gBuffer);
- configuregdata(gPosition, {GL_RGB32I}, GL_RGB_INTEGER, GL_COLOR_ATTACHMENT0);
- configuregdata(gNormal, {GL_RGB8_SNORM, GL_RGB16F}, GL_RGB, GL_COLOR_ATTACHMENT1);
- configuregdata(gAlbedoSpec, {GL_RGB8}, GL_RGB, GL_COLOR_ATTACHMENT2);
- constexpr std::array<unsigned int, 3> attachments {
- GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2};
- glDrawBuffers(attachments.size(), attachments.data());
+ configureAttachment(gBuffer, gPosition, GL_RGB32F, GL_COLOR_ATTACHMENT0);
+ configureAttachment(gBuffer, gNormal, GL_RGB8_SNORM, GL_COLOR_ATTACHMENT1);
+ configureAttachment(gBuffer, gAlbedoSpec, GL_RGB8, GL_COLOR_ATTACHMENT2);
- glBindRenderbuffer(GL_RENDERBUFFER, depth);
- glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, size.x, size.y);
- glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depth);
+ depth.storage(GL_DEPTH_COMPONENT, size);
+ gBuffer.buffer(GL_DEPTH_ATTACHMENT, depth);
+ gBuffer.assertComplete();
- glBindFramebuffer(GL_FRAMEBUFFER, gBufferIll);
- configuregdata(gIllumination, {GL_RGB8}, GL_RGB, GL_COLOR_ATTACHMENT0);
- glDrawBuffer(GL_COLOR_ATTACHMENT0);
+ configureAttachment(gBufferIll, gIllumination, GL_RGB8, GL_COLOR_ATTACHMENT0);
+ gBufferIll.assertComplete();
+}
- glBindFramebuffer(GL_FRAMEBUFFER, output);
+void
+SceneRenderer::preFrame(const SceneProvider & scene, const LightDirection lightDirection)
+{
+ glDebugScope _ {output};
+ const auto lightView = shadowMapper.preFrame(lightDirection, camera);
+ billboardPainter.setView(std::asin(camera.getForward().z), camera.getView());
+ scene.forEachRenderable([&lightView, this](Renderable * renderable) {
+ renderable->preFrame(camera, lightView);
+ renderable->updateBillboard(billboardPainter);
+ });
+ if (auto cld = Renderable::commonLocationData.lock()) {
+ glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, cld->bufferName());
+ glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, cld->indexBufferName());
+ }
}
void
SceneRenderer::render(const SceneProvider & scene) const
{
+ glDebugScope _ {output};
shader.setViewProjection(camera.getPosition(), camera.getViewProjection());
glViewport(0, 0, size.x, size.y);
- // Geometry pass
- glBindFramebuffer(GL_FRAMEBUFFER, gBuffer);
- glEnable(GL_BLEND);
- glEnable(GL_CULL_FACE);
- glCullFace(GL_BACK);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- glEnable(GL_DEPTH_TEST);
- glClearColor(0, 0, 0, 1);
- glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
- scene.content(shader);
+ if (glDebugScope _ {gBuffer, "Geometry/colour pass"}) {
+ // Geometry/colour pass - writes albedo, normal and position textures
+ glBindFramebuffer(GL_FRAMEBUFFER, gBuffer);
+ glEnable(GL_BLEND);
+ glEnable(GL_CULL_FACE);
+ glCullFace(GL_BACK);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glEnable(GL_DEPTH_TEST);
+ glClearColor(0, 0, 0, 1);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+ glEnable(GL_PROGRAM_POINT_SIZE);
+ scene.content(shader, camera);
+ }
- // Illumination pass
- glBindFramebuffer(GL_FRAMEBUFFER, gBufferIll);
- glBlendFunc(GL_ONE, GL_ONE);
- glActiveTexture(GL_TEXTURE0);
- glBindTexture(GL_TEXTURE_2D, gPosition);
- glActiveTexture(GL_TEXTURE1);
- glBindTexture(GL_TEXTURE_2D, gNormal);
- glActiveTexture(GL_TEXTURE2);
- glBindTexture(GL_TEXTURE_2D_ARRAY, shadowMapper);
- scene.environment(shader, *this);
- glDisable(GL_DEPTH_TEST);
- scene.lights(shader);
+ if (glDebugScope _ {gBufferIll, "Environment pass"}) {
+ // Environment pass -
+ // * ambient - clears illumination texture - see setAmbientLight
+ // * directional - updates shadowMapper, reads normal and position, writes illumination - see
+ // setDirectionalLight
+ scene.environment(shader, *this);
+ }
- // Lighting pass
- glBindFramebuffer(GL_FRAMEBUFFER, output);
- glViewport(0, 0, size.x, size.y);
- glCullFace(GL_BACK);
- glDisable(GL_BLEND);
- glDisable(GL_DEPTH_TEST);
- glActiveTexture(GL_TEXTURE2);
- glBindTexture(GL_TEXTURE_2D, gAlbedoSpec);
- glActiveTexture(GL_TEXTURE3);
- glBindTexture(GL_TEXTURE_2D, gIllumination);
- lighting.use();
- renderQuad();
+ if (glDebugScope _ {gBufferIll, "Scene lighting pass"}) {
+ // Scene lights pass -
+ // * per light - reads normal and position, writes illumination
+ glBindFramebuffer(GL_FRAMEBUFFER, gBufferIll);
+ glBlendFunc(GL_ONE, GL_ONE);
+ gPosition.bind(0);
+ gNormal.bind(1);
+ shadowMapper.bind(2);
+ glDisable(GL_DEPTH_TEST);
+ scene.lights(shader);
+ }
+
+ if (glDebugScope _ {output, "Composition pass"}) {
+ // Composition pass - reads albedo and illumination, writes output
+ glBindFramebuffer(GL_FRAMEBUFFER, output);
+ glViewport(0, 0, size.x, size.y);
+ glCullFace(GL_BACK);
+ glDisable(GL_BLEND);
+ glDisable(GL_DEPTH_TEST);
+ gAlbedoSpec.bind(2);
+ gIllumination.bind(3);
+ lighting.use();
+ renderQuad();
+ }
}
void
SceneRenderer::setAmbientLight(const RGB & colour) const
{
+ glDebugScope _ {output};
glBindFramebuffer(GL_FRAMEBUFFER, gBufferIll);
glClearColor(colour.r, colour.g, colour.b, 1.0F);
glClear(GL_COLOR_BUFFER_BIT);
}
void
-SceneRenderer::setDirectionalLight(const RGB & colour, const Direction3D & direction, const SceneProvider & scene) const
+SceneRenderer::setDirectionalLight(
+ const RGB & colour, const LightDirection & direction, const SceneProvider & scene) const
{
if (colour.r > 0 || colour.g > 0 || colour.b > 0) {
+ glDebugScope _ {output};
const auto lvp = shadowMapper.update(scene, direction, camera);
glBindFramebuffer(GL_FRAMEBUFFER, gBufferIll);
+ glBlendFunc(GL_ONE, GL_ONE);
+ gPosition.bind(0);
+ gNormal.bind(1);
+ shadowMapper.bind(2);
glViewport(0, 0, size.x, size.y);
dirLight.use();
- dirLight.setDirectionalLight(colour, direction, camera.getPosition(), lvp);
+ dirLight.setDirectionalLight(colour, direction.vector(), lvp);
renderQuad();
}
}
@@ -129,22 +175,20 @@ SceneRenderer::renderQuad() const
glBindVertexArray(0);
}
-SceneRenderer::DirectionalLightProgram::DirectionalLightProgram() : Program {lighting_vs, directionalLight_fs} { }
+SceneRenderer::DirectionalLightProgram::DirectionalLightProgram() : Program {lighting_vert, directionalLight_frag} { }
const auto toTextureSpaceMat = glm::translate(glm::identity<glm::mat4>(), glm::vec3 {0.5F})
* glm::scale(glm::identity<glm::mat4>(), glm::vec3 {0.5F});
void
SceneRenderer::DirectionalLightProgram::setDirectionalLight(
- const RGB & c, const Direction3D & d, const GlobalPosition3D & p, const std::span<const glm::mat4x4> lvp) const
+ const RGB & c, const Direction3D & d, const std::span<const glm::mat4x4> lvp) const
{
const auto toTextureSpace = [](const glm::mat4 & m) {
return toTextureSpaceMat * m;
};
glUniform(colourLoc, c);
- const auto nd = glm::normalize(d);
- glUniform(directionLoc, nd);
- glUniform(lightPointLoc, p);
+ glUniform(directionLoc, d);
glUniform(lightViewProjectionCountLoc, static_cast<GLuint>(lvp.size()));
glUniform(lightViewProjectionLoc, std::span<const glm::mat4> {lvp * toTextureSpace});
}
diff --git a/gfx/gl/sceneRenderer.h b/gfx/gl/sceneRenderer.h
index 4195bcf..2901dea 100644
--- a/gfx/gl/sceneRenderer.h
+++ b/gfx/gl/sceneRenderer.h
@@ -1,31 +1,39 @@
#pragma once
-#include "camera.h"
-#include "glArrays.h"
+#include "billboardPainter.h"
+#include "gfx/gl/glFramebuffer.h"
+#include "gfx/lightDirection.h"
+#include "gldebug.h"
#include "program.h"
#include "sceneProvider.h"
#include "sceneShader.h"
#include "shadowMapper.h"
+#include <gfx/camera.h>
#include <glm/fwd.hpp>
class SceneRenderer {
public:
- explicit SceneRenderer(ScreenAbsCoord size, GLuint output);
+ SceneRenderer(ScreenAbsCoord size, GLuint output);
+ SceneRenderer(ScreenAbsCoord size, GLuint output, glDebugScope);
+ void resize(ScreenAbsCoord size);
+
+ void preFrame(const SceneProvider & scene, LightDirection lightDirection);
void render(const SceneProvider &) const;
void setAmbientLight(const RGB & colour) const;
- void setDirectionalLight(const RGB & colour, const Direction3D & direction, const SceneProvider &) const;
+ void setDirectionalLight(const RGB & colour, const LightDirection & direction, const SceneProvider &) const;
Camera camera;
protected:
void renderQuad() const;
+ void configureBuffers();
ScreenAbsCoord size;
GLuint output;
- glFrameBuffer gBuffer, gBufferIll;
- glTexture gPosition, gNormal, gAlbedoSpec, gIllumination;
- glRenderBuffer depth;
+ glFramebuffer gBuffer, gBufferIll;
+ glTexture<GL_TEXTURE_2D> gPosition, gNormal, gAlbedoSpec, gIllumination;
+ glRenderbuffer depth;
class DeferredLightProgram : public Program {
public:
@@ -38,13 +46,11 @@ protected:
DirectionalLightProgram();
using Program::use;
- void setDirectionalLight(
- const RGB &, const Direction3D &, const GlobalPosition3D &, const std::span<const glm::mat4x4>) const;
+ void setDirectionalLight(const RGB &, const Direction3D &, const std::span<const glm::mat4x4>) const;
private:
RequiredUniformLocation directionLoc {*this, "lightDirection"};
RequiredUniformLocation colourLoc {*this, "lightColour"};
- RequiredUniformLocation lightPointLoc {*this, "lightPoint"};
RequiredUniformLocation lightViewProjectionLoc {*this, "lightViewProjection"};
RequiredUniformLocation lightViewProjectionCountLoc {*this, "lightViewProjectionCount"};
};
@@ -55,4 +61,5 @@ protected:
glBuffer displayVBO;
SceneShader shader;
ShadowMapper shadowMapper;
+ BillboardPainter billboardPainter;
};
diff --git a/gfx/gl/sceneShader.cpp b/gfx/gl/sceneShader.cpp
index 4cbccb3..302edda 100644
--- a/gfx/gl/sceneShader.cpp
+++ b/gfx/gl/sceneShader.cpp
@@ -1,24 +1,28 @@
#include "sceneShader.h"
-#include <gfx/gl/shaders/fs-landmass.h>
-#include <gfx/gl/shaders/fs-material.h>
-#include <gfx/gl/shaders/fs-network.h>
-#include <gfx/gl/shaders/fs-pointLight.h>
-#include <gfx/gl/shaders/fs-spotLight.h>
-#include <gfx/gl/shaders/fs-water.h>
-#include <gfx/gl/shaders/gs-networkCurve.h>
-#include <gfx/gl/shaders/gs-networkStraight.h>
-#include <gfx/gl/shaders/gs-pointLight.h>
-#include <gfx/gl/shaders/gs-spotLight.h>
-#include <gfx/gl/shaders/vs-dynamicPoint.h>
-#include <gfx/gl/shaders/vs-dynamicPointInst.h>
-#include <gfx/gl/shaders/vs-fixedPoint.h>
-#include <gfx/gl/shaders/vs-landmass.h>
-#include <gfx/gl/shaders/vs-networkCurve.h>
-#include <gfx/gl/shaders/vs-networkStraight.h>
-#include <gfx/gl/shaders/vs-pointLight.h>
-#include <gfx/gl/shaders/vs-spotLight.h>
-#include <gfx/gl/shaders/vs-water.h>
-#include <gfx/gl/vertexArrayObject.h>
+#include <gfx/gl/shaders/billboard-frag.h>
+#include <gfx/gl/shaders/billboard-vert.h>
+#include <gfx/gl/shaders/dynamicPoint-vert.h>
+#include <gfx/gl/shaders/dynamicPointInst-vert.h>
+#include <gfx/gl/shaders/fixedPoint-vert.h>
+#include <gfx/gl/shaders/landmass-frag.h>
+#include <gfx/gl/shaders/landmass-vert.h>
+#include <gfx/gl/shaders/material-frag.h>
+#include <gfx/gl/shaders/network-frag.h>
+#include <gfx/gl/shaders/networkCurve-geom.h>
+#include <gfx/gl/shaders/networkCurve-tesc.h>
+#include <gfx/gl/shaders/networkCurve-tese.h>
+#include <gfx/gl/shaders/networkCurve-vert.h>
+#include <gfx/gl/shaders/networkStraight-geom.h>
+#include <gfx/gl/shaders/networkStraight-vert.h>
+#include <gfx/gl/shaders/pointLight-frag.h>
+#include <gfx/gl/shaders/pointLight-geom.h>
+#include <gfx/gl/shaders/pointLight-vert.h>
+#include <gfx/gl/shaders/spotLight-frag.h>
+#include <gfx/gl/shaders/spotLight-geom.h>
+#include <gfx/gl/shaders/spotLight-vert.h>
+#include <gfx/gl/shaders/water-frag.h>
+#include <gfx/gl/shaders/water-vert.h>
+#include <gl_traits.h>
#include <glm/gtc/type_ptr.hpp>
#include <glm/gtx/transform.hpp>
#include <location.h>
@@ -28,17 +32,17 @@ inline void
SceneShader::allPrograms(auto member, auto &&... ps) const
{
for (const auto & prog : std::initializer_list<const SceneProgram *> {&basic, &basicInst, &water, &landmass,
- &absolute, &pointLightInst, &spotLightInst, &networkStraight, &networkCurve}) {
+ &absolute, &pointLightInst, &spotLightInst, &networkStraight, &networkCurve, &billboard}) {
(prog->*member)(std::forward<decltype(ps)>(ps)...);
}
}
SceneShader::SceneShader() :
- basicInst {dynamicPointInst_vs, material_fs}, landmass {landmass_vs, landmass_fs},
- absolute {fixedPoint_vs, material_fs}, spotLightInst {spotLight_vs, spotLight_gs, spotLight_fs},
- pointLightInst {pointLight_vs, pointLight_gs, pointLight_fs},
- networkStraight {networkStraight_vs, networkStraight_gs, network_fs},
- networkCurve {networkCurve_vs, networkCurve_gs, network_fs}
+ basicInst {dynamicPointInst_vert, material_frag}, absolute {fixedPoint_vert, material_frag},
+ spotLightInst {spotLight_vert, spotLight_geom, spotLight_frag},
+ pointLightInst {pointLight_vert, pointLight_geom, pointLight_frag}, landmass {landmass_vert, landmass_frag},
+ networkStraight {networkStraight_vert, networkStraight_geom, network_frag},
+ networkCurve {networkCurve_vert, networkCurve_tesc, networkCurve_tese, networkCurve_geom, network_frag}
{
}
@@ -65,13 +69,13 @@ SceneShader::SceneProgram::setViewProjection(const GlobalPosition3D & viewPoint,
void
SceneShader::SceneProgram::setViewPort(const ViewPort & viewPort) const
{
- if (viewPortLoc >= 0) {
+ if (viewPortLoc) {
glUseProgram(*this);
glUniform(viewPortLoc, viewPort);
}
}
-SceneShader::BasicProgram::BasicProgram() : SceneProgram {dynamicPoint_vs, material_fs} { }
+SceneShader::BasicProgram::BasicProgram() : SceneProgram {dynamicPoint_vert, material_frag} { }
void
SceneShader::BasicProgram::setModel(Location const & location) const
@@ -87,6 +91,23 @@ SceneShader::BasicProgram::use(Location const & location) const
setModel(location);
}
+SceneShader::BillboardProgram::BillboardProgram() : SceneProgram {billboard_vert, billboard_frag} { }
+
+void
+SceneShader::BillboardProgram::use(RelativeDistance size, RelativePosition3D centre) const
+{
+ Program::use();
+ glUniform(sizeLoc, size);
+ glUniform(centreLoc, centre);
+}
+
+void
+SceneShader::LandmassProgram::use(const glm::vec3 colourBias) const
+{
+ Program::use();
+ glUniform(colourBiasLos, colourBias);
+}
+
void
SceneShader::NetworkProgram::use(
const std::span<const glm::vec3> profile, const std::span<const float> texturePos) const
@@ -97,7 +118,7 @@ SceneShader::NetworkProgram::use(
glUniform(profileLengthLoc, static_cast<GLuint>(profile.size()));
}
-SceneShader::WaterProgram::WaterProgram() : SceneProgram {water_vs, water_fs} { }
+SceneShader::WaterProgram::WaterProgram() : SceneProgram {water_vert, water_frag} { }
void
SceneShader::WaterProgram::use(float waveCycle) const
diff --git a/gfx/gl/sceneShader.h b/gfx/gl/sceneShader.h
index 51f0e21..faba4a4 100644
--- a/gfx/gl/sceneShader.h
+++ b/gfx/gl/sceneShader.h
@@ -32,6 +32,17 @@ class SceneShader {
RequiredUniformLocation modelPosLoc {*this, "modelPos"};
};
+ class BillboardProgram : public SceneProgram {
+ public:
+ BillboardProgram();
+
+ void use(RelativeDistance size, RelativePosition3D centre) const;
+
+ private:
+ RequiredUniformLocation sizeLoc {*this, "size"};
+ RequiredUniformLocation centreLoc {*this, "centre"};
+ };
+
class AbsolutePosProgram : public SceneProgram {
public:
using Program::use;
@@ -50,6 +61,15 @@ class SceneShader {
RequiredUniformLocation profileLengthLoc {*this, "profileLength"};
};
+ class LandmassProgram : public AbsolutePosProgram {
+ public:
+ using AbsolutePosProgram::AbsolutePosProgram;
+ void use(const glm::vec3) const;
+
+ private:
+ RequiredUniformLocation colourBiasLos {*this, "colourBias"};
+ };
+
class WaterProgram : public SceneProgram {
public:
WaterProgram();
@@ -64,7 +84,9 @@ public:
BasicProgram basic;
WaterProgram water;
- AbsolutePosProgram basicInst, landmass, absolute, spotLightInst, pointLightInst;
+ AbsolutePosProgram basicInst, absolute, spotLightInst, pointLightInst;
+ BillboardProgram billboard;
+ LandmassProgram landmass;
NetworkProgram networkStraight, networkCurve;
void setViewProjection(const GlobalPosition3D & viewPoint, const glm::mat4 & viewProjection) const;
diff --git a/gfx/gl/shader.cpp b/gfx/gl/shader.cpp
index 931c372..319726f 100644
--- a/gfx/gl/shader.cpp
+++ b/gfx/gl/shader.cpp
@@ -8,17 +8,18 @@
namespace {
auto
- getInt(GLenum e)
+ getInt(GLenum pname)
{
- GLint i {};
- glGetIntegerv(e, &i);
- return std::to_string(i);
+ GLint data {};
+ glGetIntegerv(pname, &data);
+ return std::to_string(data);
}
using LookUpFunction = std::string (*)(GLenum);
- constexpr std::array<std::tuple<std::string_view, GLenum, LookUpFunction>, 1> LOOKUPS {{
+ constexpr auto LOOKUPS = std::to_array<std::tuple<std::string_view, GLenum, LookUpFunction>>({
{"GL_MAX_GEOMETRY_OUTPUT_VERTICES", GL_MAX_GEOMETRY_OUTPUT_VERTICES, getInt},
- }};
+ {"GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS", GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS, getInt},
+ });
struct ShaderCompileError : public MsgException<std::invalid_argument> {
explicit ShaderCompileError(GLuint shader, Shader::Source src) :
@@ -63,8 +64,8 @@ Shader::compile() const
};
if (lookups) {
std::basic_string<GLchar> textMod {text};
- for (const auto & match : ctre::range<R"(\bGL_[A-Z_]+\b)">(textMod)) {
- if (const auto lookup = std::find_if(LOOKUPS.begin(), LOOKUPS.end(),
+ while (const auto match = ctre::search<R"(\bGL_[A-Z_]+\b)">(textMod)) {
+ if (const auto * const lookup = std::find_if(LOOKUPS.begin(), LOOKUPS.end(),
[&match](const auto & lookup) {
return std::get<std::string_view>(lookup) == match;
});
@@ -72,6 +73,9 @@ Shader::compile() const
const auto & [name, pname, getFunction] = *lookup;
textMod.replace(match.begin(), match.end(), getFunction(pname));
}
+ else {
+ throw std::domain_error(std::format("Unknown shader constant: {}", match.view()));
+ }
}
source(textMod.c_str(), static_cast<GLint>(textMod.length()));
}
diff --git a/gfx/gl/shaders/billboard.frag b/gfx/gl/shaders/billboard.frag
new file mode 100644
index 0000000..d5610de
--- /dev/null
+++ b/gfx/gl/shaders/billboard.frag
@@ -0,0 +1,29 @@
+#version 460 core
+#extension GL_ARB_shading_language_include : enable
+
+const float tau = 6.28318531;
+
+layout(binding = 0) uniform sampler2DArray billboardDepth;
+layout(binding = 1) uniform sampler2DArray billboardNormal;
+layout(binding = 2) uniform sampler2DArray billboardAlbedo;
+uniform mat4 viewProjection;
+uniform float size;
+
+#include "materialOut.glsl"
+
+flat in vec3 ModelPos;
+flat in float Yaw;
+flat in float Depth;
+
+void
+main()
+{
+ int viewAngle = int(round(8 * Yaw / tau)) % 8;
+ vec3 texel = vec3(gl_PointCoord * vec2(-1, 1) + vec2(1, 0), viewAngle);
+ gAlbedoSpec = texture(billboardAlbedo, texel);
+ if (gAlbedoSpec.a < 0.5) {
+ discard;
+ }
+ gPosition = ivec4(ModelPos + vec3(0, 0, size * 2 * (1 - gl_PointCoord.y)), 1);
+ gNormal = texture(billboardNormal, texel) * vec4(-1, -1, 1, 1);
+}
diff --git a/gfx/gl/shaders/billboard.vert b/gfx/gl/shaders/billboard.vert
new file mode 100644
index 0000000..d6d3869
--- /dev/null
+++ b/gfx/gl/shaders/billboard.vert
@@ -0,0 +1,27 @@
+#version 460 core
+#extension GL_ARB_shading_language_include : enable
+
+#include "commonLocationData.glsl"
+
+uniform mat4 viewProjection;
+uniform ivec4 viewPort;
+uniform ivec3 viewPoint;
+uniform vec3 centre;
+uniform float size;
+
+layout(location = 0) in uint index;
+
+flat out vec3 ModelPos;
+flat out float Yaw;
+flat out float Depth;
+
+void
+main()
+{
+ const ivec3 modelPos = locations[cldIndex[index]].position.xyz;
+ ModelPos = modelPos - viewPoint;
+ Yaw = locations[cldIndex[index]].rotation.x;
+ gl_Position = viewProjection * vec4(ModelPos + centre, 1);
+ Depth = gl_Position.w;
+ gl_PointSize = (viewPort.w * size * 2) / gl_Position.w;
+}
diff --git a/gfx/gl/shaders/billboardPainter.frag b/gfx/gl/shaders/billboardPainter.frag
new file mode 100644
index 0000000..cba294f
--- /dev/null
+++ b/gfx/gl/shaders/billboardPainter.frag
@@ -0,0 +1,23 @@
+#version 460 core
+#extension GL_ARB_shading_language_include : enable
+
+layout(binding = 0) uniform sampler2D textureAlbedo;
+layout(location = 0) out vec4 bNormal;
+layout(location = 1) out vec4 bAlbedoSpec;
+
+#include "materialCommon.glsl"
+#include "materialDetail.glsl"
+in vec2 gTexCoords;
+in vec3 gNormal;
+in vec4 gColour;
+flat in MaterialDetail gMaterial;
+
+void
+main()
+{
+ vec4 textureColour = getTextureColour(gMaterial, gTexCoords);
+ float opaque = step(0.5, mix(textureColour.a, 1, gColour.a));
+ bNormal = vec4(gNormal, opaque);
+ gl_FragDepth = mix(1.0, gl_FragCoord.z, opaque);
+ bAlbedoSpec = mix(textureColour, vec4(gColour.rgb, 1), gColour.a);
+}
diff --git a/gfx/gl/shaders/billboardPainter.geom b/gfx/gl/shaders/billboardPainter.geom
new file mode 100644
index 0000000..aa4ad81
--- /dev/null
+++ b/gfx/gl/shaders/billboardPainter.geom
@@ -0,0 +1,35 @@
+#version 460 core
+#extension GL_ARB_shading_language_include : enable
+
+#include "materialDetail.glsl"
+
+layout(triangles) in;
+layout(triangle_strip, max_vertices = 24) out;
+
+uniform mat4 viewProjection[8];
+uniform mat4 view[8];
+in vec3 FragPos[];
+in vec2 TexCoords[];
+flat in MaterialDetail Material[];
+in vec3 Normal[];
+in vec4 Colour[];
+out vec2 gTexCoords;
+out vec3 gNormal;
+out vec4 gColour;
+flat out MaterialDetail gMaterial;
+
+void
+main()
+{
+ for (gl_Layer = 0; gl_Layer < viewProjection.length(); ++gl_Layer) {
+ for (int v = 0; v < FragPos.length(); ++v) {
+ gl_Position = viewProjection[gl_Layer] * vec4(FragPos[v], 1);
+ gNormal = (view[gl_Layer] * vec4(Normal[v], 1)).xyz;
+ gTexCoords = TexCoords[v];
+ gMaterial = Material[v];
+ gColour = Colour[v];
+ EmitVertex();
+ }
+ EndPrimitive();
+ }
+}
diff --git a/gfx/gl/shaders/billboardPainter.vert b/gfx/gl/shaders/billboardPainter.vert
new file mode 100644
index 0000000..a2d0b2e
--- /dev/null
+++ b/gfx/gl/shaders/billboardPainter.vert
@@ -0,0 +1,24 @@
+#version 460 core
+#extension GL_ARB_shading_language_include : enable
+
+layout(binding = 1) uniform usampler2DRect materialData;
+
+#include "getMaterialDetail.glsl"
+#include "materialDetail.glsl"
+#include "meshIn.glsl"
+
+out vec3 FragPos;
+out vec2 TexCoords;
+flat out MaterialDetail Material;
+out vec3 Normal;
+out vec4 Colour;
+
+void
+main()
+{
+ TexCoords = texCoord;
+ Material = getMaterialDetail(material);
+ FragPos = position;
+ Colour = colour;
+ Normal = normal;
+}
diff --git a/gfx/gl/shaders/commonLocationData.glsl b/gfx/gl/shaders/commonLocationData.glsl
new file mode 100644
index 0000000..4939b9b
--- /dev/null
+++ b/gfx/gl/shaders/commonLocationData.glsl
@@ -0,0 +1,20 @@
+#ifndef COMMON_LOCATION_DATA_INCLUDED
+#define COMMON_LOCATION_DATA_INCLUDED
+
+struct CommonLocationData {
+ ivec4 position;
+ vec4 rotation;
+ mat3x4 rotationMatrix;
+};
+
+layout(binding = 0, std430) restrict readonly buffer commonLocationData
+{
+ CommonLocationData locations[];
+};
+
+layout(binding = 1, std430) restrict readonly buffer commonLocationDataIndex
+{
+ uint cldIndex[];
+};
+
+#endif
diff --git a/gfx/gl/shaders/commonPoint.glsl b/gfx/gl/shaders/commonPoint.glsl
index dc534d5..7c1a521 100644
--- a/gfx/gl/shaders/commonPoint.glsl
+++ b/gfx/gl/shaders/commonPoint.glsl
@@ -1,13 +1,18 @@
-include(`getMaterialDetail.glsl')
+#ifndef COMMON_POINT_INCLUDED
+#define COMMON_POINT_INCLUDED
+
+#include "getMaterialDetail.glsl"
void
main()
{
- FragPos = (model * position) + modelPos;
+ FragPos = (model * position) + modelPos - viewPoint;
TexCoords = texCoord;
Normal = (model * normal);
Colour = colour;
Material = getMaterialDetail(material);
- gl_Position = viewProjection * vec4(FragPos - viewPoint, 1);
+ gl_Position = viewProjection * vec4(FragPos, 1);
}
+
+#endif
diff --git a/gfx/gl/shaders/commonShadowPoint.gs b/gfx/gl/shaders/commonShadowPoint-geom.glsl
index 2413cc0..f373834 100644
--- a/gfx/gl/shaders/commonShadowPoint.gs
+++ b/gfx/gl/shaders/commonShadowPoint-geom.glsl
@@ -1,7 +1,5 @@
-#version 330 core
-#extension GL_ARB_viewport_array : enable
-
-ifdef(`TEXTURES', include(`materialDetail.glsl'))
+#ifndef COMMON_SHADOW_POINT_GEOM_INCLUDED
+#define COMMON_SHADOW_POINT_GEOM_INCLUDED
uniform mat4 viewProjection[4];
uniform int viewProjections;
@@ -9,8 +7,14 @@ in vec4 vworldPos[];
layout(triangles) in;
layout(triangle_strip, max_vertices = 12) out;
-ifdef(`TEXTURES', in vec2 TexCoords[]; out vec2 texCoord;)
-ifdef(`TEXTURES', flat in MaterialDetail Material[]; flat out MaterialDetail material;)
+#ifdef TEXTURES
+# include "materialDetail.glsl"
+
+in vec2 TexCoords[];
+out vec2 texCoord;
+flat in MaterialDetail Material[];
+flat out MaterialDetail material;
+#endif
void
main()
@@ -20,10 +24,14 @@ main()
gl_Position = viewProjection[vp] * vworldPos[v];
gl_Position.z = max(gl_Position.z, -1);
gl_Layer = vp;
- ifdef(`TEXTURES', texCoord = TexCoords[v];)
- ifdef(`TEXTURES', material = Material[v];)
+#ifdef TEXTURES
+ texCoord = TexCoords[v];
+ material = Material[v];
+#endif
EmitVertex();
}
EndPrimitive();
}
}
+
+#endif
diff --git a/gfx/gl/shaders/commonShadowPoint.geom b/gfx/gl/shaders/commonShadowPoint.geom
new file mode 100644
index 0000000..519dc62
--- /dev/null
+++ b/gfx/gl/shaders/commonShadowPoint.geom
@@ -0,0 +1,4 @@
+#version 460
+#extension GL_ARB_shading_language_include : enable
+
+#include "commonShadowPoint-geom.glsl"
diff --git a/gfx/gl/shaders/commonShadowPoint.glsl b/gfx/gl/shaders/commonShadowPoint.glsl
index 9910d46..e0b71a8 100644
--- a/gfx/gl/shaders/commonShadowPoint.glsl
+++ b/gfx/gl/shaders/commonShadowPoint.glsl
@@ -1,3 +1,6 @@
+#ifndef COMMON_SHADOW_POINT_INCLUDED
+#define COMMON_SHADOW_POINT_INCLUDED
+
out vec4 vworldPos;
void
@@ -5,6 +8,10 @@ main()
{
vec3 worldPos = model * position;
vworldPos = vec4(worldPos - viewPoint + modelPos, 1);
- ifdef(`TEXTURES', TexCoords = texCoord;);
- ifdef(`TEXTURES', Material = getMaterialDetail(material););
+#ifdef TEXTURES
+ TexCoords = texCoord;
+ Material = getMaterialDetail(material);
+#endif
}
+
+#endif
diff --git a/gfx/gl/shaders/directionalLight.frag b/gfx/gl/shaders/directionalLight.frag
new file mode 100644
index 0000000..5da1acd
--- /dev/null
+++ b/gfx/gl/shaders/directionalLight.frag
@@ -0,0 +1,59 @@
+#version 460 core
+
+const int MAX_MAPS = 4;
+
+out vec3 FragColor;
+
+in vec2 TexCoords;
+
+layout(binding = 0) uniform sampler2D gPosition;
+layout(binding = 1) uniform sampler2D gNormal;
+layout(binding = 2) uniform sampler2DArray shadowMap;
+
+uniform vec3 lightDirection;
+uniform vec3 lightColour;
+uniform mat4 lightViewProjection[MAX_MAPS];
+uniform uint lightViewProjectionCount;
+
+float
+getShadow(vec3 positionInLightSpace, float m, vec2 texelSize)
+{
+ float shadow = 0.0;
+ for (float x = -texelSize.x; x <= texelSize.x; x += texelSize.x) {
+ for (float y = -texelSize.y; y <= texelSize.y; y += texelSize.y) {
+ const float lightSpaceDepth = texture(shadowMap, vec3(positionInLightSpace.xy + vec2(x, y), m)).r;
+ shadow += step(positionInLightSpace.z, lightSpaceDepth + 0.001);
+ }
+ }
+ return shadow / 9.0;
+}
+
+float
+insideShadowCube(vec3 v, vec2 texelSize)
+{
+ const vec3 s = step(vec3(texelSize, 0), v) - step(vec3(1 - texelSize, 1), v);
+ return s.x * s.y * s.z;
+}
+
+float
+isShaded(vec4 Position)
+{
+ const vec2 texelSize = 1.0 / textureSize(shadowMap, 0).xy;
+ for (uint m = 0u; m < lightViewProjectionCount; m++) {
+ const vec3 positionInLightSpace = (lightViewProjection[m] * Position).xyz;
+ const float inside = insideShadowCube(positionInLightSpace, texelSize);
+ if (inside > 0) {
+ return getShadow(positionInLightSpace, m, texelSize);
+ }
+ }
+ return 1.0;
+}
+
+void
+main()
+{
+ const vec4 Position = vec4(texture(gPosition, TexCoords).xyz, 1);
+ const vec3 Normal = texture(gNormal, TexCoords).rgb;
+ const float shaded = isShaded(Position);
+ FragColor = shaded * max(dot(-lightDirection, Normal) * lightColour, 0);
+}
diff --git a/gfx/gl/shaders/directionalLight.fs b/gfx/gl/shaders/directionalLight.fs
deleted file mode 100644
index 86447ec..0000000
--- a/gfx/gl/shaders/directionalLight.fs
+++ /dev/null
@@ -1,50 +0,0 @@
-#version 330 core
-#extension GL_ARB_shading_language_420pack : enable
-
-const int MAX_MAPS = 4;
-
-out vec3 FragColor;
-
-in vec2 TexCoords;
-
-layout(binding = 0) uniform isampler2D gPosition;
-layout(binding = 1) uniform sampler2D gNormal;
-layout(binding = 2) uniform sampler2DArray shadowMap;
-
-uniform vec3 lightDirection;
-uniform vec3 lightColour;
-uniform ivec3 lightPoint;
-uniform mat4 lightViewProjection[MAX_MAPS];
-uniform uint lightViewProjectionCount;
-
-const vec3 e1 = vec3(0, 0, 0), e2 = vec3(1, 1, 1);
-
-float
-insideShadowCube(vec3 v)
-{
- const vec3 s = step(e1, v) - step(e2, v);
- return s.x * s.y * s.z;
-}
-
-float
-isShaded(vec4 Position)
-{
- for (uint m = 0u; m < lightViewProjectionCount; m++) {
- const vec3 PositionInLightSpace = (lightViewProjection[m] * Position).xyz;
- const float inside = insideShadowCube(PositionInLightSpace);
- if (inside > 0) {
- const float lightSpaceDepth = texture(shadowMap, vec3(PositionInLightSpace.xy, m)).r;
- return step(PositionInLightSpace.z, lightSpaceDepth + 0.001);
- }
- }
- return 1;
-}
-
-void
-main()
-{
- const vec4 Position = vec4(texture(gPosition, TexCoords).xyz - lightPoint, 1);
- const vec3 Normal = texture(gNormal, TexCoords).rgb;
- const float shaded = isShaded(Position);
- FragColor = shaded * max(dot(-lightDirection, Normal) * lightColour, 0);
-}
diff --git a/gfx/gl/shaders/dynamicPoint.vs b/gfx/gl/shaders/dynamicPoint.vert
index 97d2983..fb4c7fd 100644
--- a/gfx/gl/shaders/dynamicPoint.vs
+++ b/gfx/gl/shaders/dynamicPoint.vert
@@ -1,14 +1,14 @@
-#version 330 core
-#extension GL_ARB_shading_language_420pack : enable
+#version 460 core
+#extension GL_ARB_shading_language_include : enable
layout(binding = 1) uniform usampler2DRect materialData;
-include(`meshIn.glsl')
-include(`materialInterface.glsl')
+#include "materialInterface.glsl"
+#include "meshIn.glsl"
uniform mat4 viewProjection;
uniform ivec3 viewPoint;
uniform mat3 model;
uniform ivec3 modelPos;
-include(`commonPoint.glsl')
+#include "commonPoint.glsl"
diff --git a/gfx/gl/shaders/dynamicPointInst.vert b/gfx/gl/shaders/dynamicPointInst.vert
new file mode 100644
index 0000000..7c50706
--- /dev/null
+++ b/gfx/gl/shaders/dynamicPointInst.vert
@@ -0,0 +1,16 @@
+#version 460 core
+#extension GL_ARB_shading_language_include : enable
+
+layout(binding = 1) uniform usampler2DRect materialData;
+
+#include "commonLocationData.glsl"
+#include "materialInterface.glsl"
+#include "meshIn.glsl"
+
+uniform mat4 viewProjection;
+uniform ivec3 viewPoint;
+layout(location = 5) in uint index;
+mat3 model = mat3(locations[cldIndex[index]].rotationMatrix);
+ivec3 modelPos = locations[cldIndex[index]].position.xyz;
+
+#include "commonPoint.glsl"
diff --git a/gfx/gl/shaders/dynamicPointInst.vs b/gfx/gl/shaders/dynamicPointInst.vs
deleted file mode 100644
index a85f9c9..0000000
--- a/gfx/gl/shaders/dynamicPointInst.vs
+++ /dev/null
@@ -1,14 +0,0 @@
-#version 330 core
-#extension GL_ARB_shading_language_420pack : enable
-
-layout(binding = 1) uniform usampler2DRect materialData;
-
-include(`meshIn.glsl')
-include(`materialInterface.glsl')
-
-uniform mat4 viewProjection;
-uniform ivec3 viewPoint;
-layout(location = 5) in mat3 model;
-layout(location = 8) in ivec3 modelPos;
-
-include(`commonPoint.glsl')
diff --git a/gfx/gl/shaders/fixedPoint.vs b/gfx/gl/shaders/fixedPoint.vert
index 435b3d1..fa9cf67 100644
--- a/gfx/gl/shaders/fixedPoint.vs
+++ b/gfx/gl/shaders/fixedPoint.vert
@@ -1,14 +1,14 @@
-#version 330 core
-#extension GL_ARB_shading_language_420pack : enable
+#version 460 core
+#extension GL_ARB_shading_language_include : enable
layout(binding = 1) uniform usampler2DRect materialData;
-include(`meshIn.glsl')
-include(`materialInterface.glsl')
+#include "materialInterface.glsl"
+#include "meshIn.glsl"
uniform mat4 viewProjection;
uniform ivec3 viewPoint;
const mat3 model = mat3(1);
const vec3 modelPos = ivec3(0);
-include(`commonPoint.glsl')
+#include "commonPoint.glsl"
diff --git a/gfx/gl/shaders/getMaterialDetail.glsl b/gfx/gl/shaders/getMaterialDetail.glsl
index f819fb2..0169e4e 100644
--- a/gfx/gl/shaders/getMaterialDetail.glsl
+++ b/gfx/gl/shaders/getMaterialDetail.glsl
@@ -1,3 +1,8 @@
+#ifndef GET_MATERIAL_DETAIL_INCLUDED
+#define GET_MATERIAL_DETAIL_INCLUDED
+
+#include "materialDetail.glsl"
+
MaterialDetail
getMaterialDetail(uint midx)
{
@@ -10,3 +15,5 @@ getMaterialDetail(uint midx)
}
return MaterialDetail(vec2(0, 0), vec2(0, 0), uvec2(0, 0));
}
+
+#endif
diff --git a/gfx/gl/shaders/landmass.fs b/gfx/gl/shaders/landmass.frag
index 55e3c24..d0a2ce3 100644
--- a/gfx/gl/shaders/landmass.fs
+++ b/gfx/gl/shaders/landmass.frag
@@ -1,12 +1,14 @@
-#version 330 core
+#version 460 core
+#extension GL_ARB_shading_language_include : enable
+
+#include "materialOut.glsl"
-include(`materialOut.glsl')
in vec3 FragPos;
in vec3 Normal;
-flat in vec3 ColourBias;
uniform sampler2D texture0;
uniform ivec3 viewPoint;
+uniform vec3 colourBias;
const vec3 grass = vec3(.1, .4, .05);
const vec3 slope = vec3(.6, .6, .4);
@@ -35,8 +37,8 @@ main()
vec3 color = texture(texture0, vec2(position.xy % 10000) / 10000.0).rgb;
int height = position.z;
- if (ColourBias.r >= 0) {
- color *= ColourBias;
+ if (colourBias.r >= 0) {
+ color *= colourBias;
}
else if (height < beachline) { // Sandy beach
color *= sand;
@@ -67,7 +69,7 @@ main()
}
}
- gPosition = ivec4(position, 1);
+ gPosition = vec4(FragPos, 1);
gNormal = vec4(Normal, 1);
gAlbedoSpec = vec4(color, 1);
}
diff --git a/gfx/gl/shaders/landmass.vs b/gfx/gl/shaders/landmass.vert
index 9617cb9..91fc7f8 100644
--- a/gfx/gl/shaders/landmass.vs
+++ b/gfx/gl/shaders/landmass.vert
@@ -1,13 +1,10 @@
-#version 330 core
-#extension GL_ARB_shading_language_420pack : enable
+#version 460 core
layout(location = 0) in ivec3 position;
layout(location = 1) in vec3 normal;
-layout(location = 2) in vec3 colourBias;
out vec3 FragPos;
out vec3 Normal;
-flat out vec3 ColourBias;
uniform mat4 viewProjection;
uniform ivec3 viewPoint;
@@ -17,7 +14,6 @@ main()
{
FragPos = position - viewPoint;
Normal = normal;
- ColourBias = colourBias;
gl_Position = viewProjection * vec4(FragPos, 1);
}
diff --git a/gfx/gl/shaders/lighting.fs b/gfx/gl/shaders/lighting.frag
index 4646b75..b5c6c8b 100644
--- a/gfx/gl/shaders/lighting.fs
+++ b/gfx/gl/shaders/lighting.frag
@@ -1,5 +1,4 @@
-#version 330 core
-#extension GL_ARB_shading_language_420pack : enable
+#version 460 core
out vec3 FragColor;
diff --git a/gfx/gl/shaders/lighting.vs b/gfx/gl/shaders/lighting.vert
index e07cd0a..1046379 100644
--- a/gfx/gl/shaders/lighting.vs
+++ b/gfx/gl/shaders/lighting.vert
@@ -1,4 +1,4 @@
-#version 330 core
+#version 460 core
in ivec4 position;
diff --git a/gfx/gl/shaders/material.fs b/gfx/gl/shaders/material.frag
index 37f2e27..16ea6d1 100644
--- a/gfx/gl/shaders/material.fs
+++ b/gfx/gl/shaders/material.frag
@@ -1,11 +1,11 @@
-#version 330 core
-#extension GL_ARB_shading_language_420pack : enable
+#version 460 core
+#extension GL_ARB_shading_language_include : enable
layout(binding = 0) uniform sampler2D textureAlbedo;
-include(`materialInterface.glsl')
-include(`materialOut.glsl')
-include(`materialCommon.glsl')
+#include "materialCommon.glsl"
+#include "materialInterface.glsl"
+#include "materialOut.glsl"
void
main()
diff --git a/gfx/gl/shaders/materialCommon.glsl b/gfx/gl/shaders/materialCommon.glsl
index 3fe2237..f905e76 100644
--- a/gfx/gl/shaders/materialCommon.glsl
+++ b/gfx/gl/shaders/materialCommon.glsl
@@ -1,3 +1,8 @@
+#ifndef MATERIAL_COMMON_INCLUDED
+#define MATERIAL_COMMON_INCLUDED
+
+#include "materialDetail.glsl"
+
float
map(uint mapmode, float value)
{
@@ -13,7 +18,7 @@ map(uint mapmode, float value)
discard;
}
}
- return 0;
+ return 0.0;
}
vec2
@@ -31,3 +36,5 @@ getTextureColour(MaterialDetail mat, vec2 uv)
}
return texture(textureAlbedo, uv);
}
+
+#endif
diff --git a/gfx/gl/shaders/materialDetail.glsl b/gfx/gl/shaders/materialDetail.glsl
index a208d50..873b343 100644
--- a/gfx/gl/shaders/materialDetail.glsl
+++ b/gfx/gl/shaders/materialDetail.glsl
@@ -1,5 +1,10 @@
+#ifndef MATERIAL_DETAIL_INCLUDED
+#define MATERIAL_DETAIL_INCLUDED
+
struct MaterialDetail {
vec2 textureOrigin;
vec2 textureSize;
uvec2 mapmode;
};
+
+#endif
diff --git a/gfx/gl/shaders/materialInterface.glsl b/gfx/gl/shaders/materialInterface.glsl
index f2ca297..926bd6c 100644
--- a/gfx/gl/shaders/materialInterface.glsl
+++ b/gfx/gl/shaders/materialInterface.glsl
@@ -1,9 +1,18 @@
-include(`materialDetail.glsl')
+#ifndef MATERIAL_INTERFACE_INCLUDED
+#define MATERIAL_INTERFACE_INCLUDED
-define(INOUT, ifelse(TYPE, .fs, in, out));
+#include "materialDetail.glsl"
+
+#ifdef GL_FRAGMENT_SHADER
+# define INOUT in
+#else
+# define INOUT out
+#endif
INOUT vec3 FragPos;
INOUT vec2 TexCoords;
INOUT vec3 Normal;
INOUT vec4 Colour;
flat INOUT MaterialDetail Material;
+
+#endif
diff --git a/gfx/gl/shaders/materialOut.glsl b/gfx/gl/shaders/materialOut.glsl
index 846825e..928edf0 100644
--- a/gfx/gl/shaders/materialOut.glsl
+++ b/gfx/gl/shaders/materialOut.glsl
@@ -1,3 +1,8 @@
-layout(location = 0) out ivec4 gPosition;
+#ifndef MATERIAL_OUT_INCLUDED
+#define MATERIAL_OUT_INCLUDED
+
+layout(location = 0) out vec4 gPosition;
layout(location = 1) out vec4 gNormal;
layout(location = 2) out vec4 gAlbedoSpec;
+
+#endif
diff --git a/gfx/gl/shaders/meshIn.glsl b/gfx/gl/shaders/meshIn.glsl
index dd84a10..2a69391 100644
--- a/gfx/gl/shaders/meshIn.glsl
+++ b/gfx/gl/shaders/meshIn.glsl
@@ -1,5 +1,10 @@
+#ifndef MESH_OUT_INCLUDED
+#define MESH_OUT_INCLUDED
+
layout(location = 0) in vec3 position;
layout(location = 1) in vec2 texCoord;
layout(location = 2) in vec3 normal;
layout(location = 3) in vec4 colour;
layout(location = 4) in uint material;
+
+#endif
diff --git a/gfx/gl/shaders/network.fs b/gfx/gl/shaders/network.frag
index 4e347b4..b7e24d2 100644
--- a/gfx/gl/shaders/network.fs
+++ b/gfx/gl/shaders/network.frag
@@ -1,17 +1,17 @@
-#version 330 core
-#extension GL_ARB_shading_language_420pack : enable
+#version 460 core
+#extension GL_ARB_shading_language_include : enable
+
+#include "materialOut.glsl"
-include(`materialOut.glsl')
in vec3 rposition;
in vec2 texCoord;
layout(binding = 0) uniform sampler2D texture0;
-uniform ivec3 viewPoint;
void
main()
{
- gPosition = ivec4(viewPoint + rposition, 1);
+ gPosition = vec4(rposition, 1);
gNormal = vec4(0, 0, 1, 1);
gAlbedoSpec = texture(texture0, texCoord);
}
diff --git a/gfx/gl/shaders/networkCommon.glsl b/gfx/gl/shaders/networkCommon.glsl
index 0bc3c1c..faa95ec 100644
--- a/gfx/gl/shaders/networkCommon.glsl
+++ b/gfx/gl/shaders/networkCommon.glsl
@@ -1,3 +1,6 @@
+#ifndef NETWORK_COMMON_INCLUDED
+#define NETWORK_COMMON_INCLUDED
+
uniform vec3[10] profile;
uniform float[10] texturePos;
uniform uint profileLength;
@@ -12,32 +15,43 @@ out vec2 texCoord;
out vec3 rposition;
float
+viewPointDist(const ivec3 position)
+{
+ return length(vec3(viewPoint - position));
+}
+
+float
segDist(const ivec3 a, const ivec3 b)
{
- return min(distance(viewPoint, a), distance(viewPoint, b));
+ return min(viewPointDist(a), viewPointDist(b));
}
-ifelse(
- TYPE, .gs,
- // Begin: Geometry shader only function
- void doVertex(const ivec3 end, const uint v, const float texY, const mat2 rot) {
- ivec3 vpos = end + ivec3(rot * profile[v].xy, profile[v].z);
- rposition = vpos - viewPoint;
- gl_Position = viewProjection * vec4(rposition, 1);
- texCoord = vec2(texturePos[v], texY);
- EmitVertex();
- }
+#ifdef GL_GEOMETRY_SHADER // Begin: Geometry shader only function
- void doSeg(const float dist, const ivec3 apos, const ivec3 bpos, const float atexY, const float btexY,
- const mat2 arot, const mat2 brot) {
- if (dist < clipDistance) {
- uint vstep = (dist < flatDistance) ? 1u : profileLength - 1u;
- for (uint v = 0u; v < profileLength; v += vstep) {
- doVertex(bpos, v, btexY, brot);
- doVertex(apos, v, atexY, arot);
- }
- EndPrimitive();
- }
+void
+doVertex(const ivec3 end, const uint v, const float texY, const mat2 rot)
+{
+ ivec3 vpos = end + ivec3(rot * profile[v].xy, profile[v].z);
+ rposition = vpos - viewPoint;
+ gl_Position = viewProjection * vec4(rposition, 1);
+ texCoord = vec2(texturePos[v], texY);
+ EmitVertex();
+}
+
+void
+doSeg(const float dist, const ivec3 apos, const ivec3 bpos, const float atexY, const float btexY, const mat2 arot,
+ const mat2 brot)
+{
+ if (dist < clipDistance) {
+ uint vstep = (dist < flatDistance) ? 1u : profileLength - 1u;
+ for (uint v = 0u; v < profileLength; v += vstep) {
+ doVertex(bpos, v, btexY, brot);
+ doVertex(apos, v, atexY, arot);
}
- // End: Geometry shader only function
-)
+ EndPrimitive();
+ }
+}
+
+#endif // End: Geometry shader only function
+
+#endif
diff --git a/gfx/gl/shaders/networkCurve.geom b/gfx/gl/shaders/networkCurve.geom
new file mode 100644
index 0000000..a6bbd13
--- /dev/null
+++ b/gfx/gl/shaders/networkCurve.geom
@@ -0,0 +1,18 @@
+#version 460 core
+#extension GL_ARB_shading_language_include : enable
+
+layout(lines) in;
+layout(triangle_strip, max_vertices = 10) out;
+
+flat in ivec3 pos[];
+flat in mat2 rot[];
+flat in float tpos[];
+flat in float dist[];
+
+#include "networkCommon.glsl"
+
+void
+main()
+{
+ doSeg(min(dist[0], dist[1]), pos[0], pos[1], tpos[0], tpos[1], rot[0], rot[1]);
+}
diff --git a/gfx/gl/shaders/networkCurve.gs b/gfx/gl/shaders/networkCurve.gs
deleted file mode 100644
index 7cb6c42..0000000
--- a/gfx/gl/shaders/networkCurve.gs
+++ /dev/null
@@ -1,47 +0,0 @@
-#version 330 core
-
-flat in ivec3 apos[];
-flat in ivec3 bpos[];
-flat in ivec3 cpos[];
-flat in float reps[];
-flat in float aangle[];
-flat in float bangle[];
-flat in float radius[];
-
-layout(points) in;
-layout(triangle_strip, max_vertices = GL_MAX_GEOMETRY_OUTPUT_VERTICES) out;
-
-const mat2 rot = mat2(1);
-
-include(`networkCommon.glsl')
-
-mat2
-getRot(float angle)
-{
- return mat2(cos(angle), sin(angle), -sin(angle), cos(angle));
-}
-
-void
-main()
-{
- float segs = clamp(
- round(reps[0] * radius[0] / 1000), 4, floor(uint(GL_MAX_GEOMETRY_OUTPUT_VERTICES) / (profileLength * 2u)));
- vec3 arcstep = vec3((bangle[0] - aangle[0]), // angle
- reps[0], // texture
- (bpos[0].z - apos[0].z)) // height
- / segs;
-
- ivec3 prevPos = apos[0];
- mat2 prevRot = getRot(aangle[0]);
- float prevTex = 0;
- for (vec3 arc = arcstep; arc.y < reps[0] - 0.01; arc += arcstep) {
- mat2 rot = getRot(arc.x + aangle[0]);
- ivec3 pos = cpos[0] + ivec3(rot * vec2(radius[0], 0), arc.z);
- float tex = arc.y;
- doSeg(segDist(prevPos, pos), pos, prevPos, tex, prevTex, rot, prevRot);
- prevPos = pos;
- prevRot = rot;
- prevTex = tex;
- }
- doSeg(segDist(prevPos, bpos[0]), bpos[0], prevPos, reps[0], prevTex, getRot(bangle[0]), prevRot);
-}
diff --git a/gfx/gl/shaders/networkCurve.tesc b/gfx/gl/shaders/networkCurve.tesc
new file mode 100644
index 0000000..5a6e449
--- /dev/null
+++ b/gfx/gl/shaders/networkCurve.tesc
@@ -0,0 +1,37 @@
+#version 460 core
+
+layout(vertices = 1) out;
+
+flat in ivec3 pos[][2];
+flat in ivec2 cpos[];
+flat in float reps[];
+flat in float angles[][2];
+flat in float radius[];
+
+flat out ivec3 c_pos[][2];
+flat out ivec2 c_cpos[];
+flat out float c_reps[];
+flat out float c_angles[][2];
+flat out float c_radius[];
+
+float
+segments()
+{
+ const float arc = angles[gl_InvocationID][0] - angles[gl_InvocationID][1];
+ const float error = 100.;
+ const float diff = acos(1.f - (error / radius[gl_InvocationID]));
+ return clamp(arc / diff, arc, 180);
+}
+
+void
+main()
+{
+ c_pos[gl_InvocationID] = pos[gl_InvocationID];
+ c_cpos[gl_InvocationID] = cpos[gl_InvocationID];
+ c_reps[gl_InvocationID] = reps[gl_InvocationID];
+ c_angles[gl_InvocationID] = angles[gl_InvocationID];
+ c_radius[gl_InvocationID] = radius[gl_InvocationID];
+
+ gl_TessLevelOuter[0] = 1;
+ gl_TessLevelOuter[1] = segments();
+}
diff --git a/gfx/gl/shaders/networkCurve.tese b/gfx/gl/shaders/networkCurve.tese
new file mode 100644
index 0000000..1331776
--- /dev/null
+++ b/gfx/gl/shaders/networkCurve.tese
@@ -0,0 +1,46 @@
+#version 460 core
+#extension GL_ARB_shading_language_include : enable
+
+layout(isolines, equal_spacing, cw) in;
+
+flat in ivec3 c_pos[][2];
+flat in ivec2 c_cpos[];
+flat in float c_reps[];
+flat in float c_angles[][2];
+flat in float c_radius[];
+
+flat out ivec3 pos;
+flat out mat2 rot;
+flat out float tpos;
+flat out float dist;
+
+const float startTolerance = 1. / 200.;
+const float endTolerance = 1. - startTolerance;
+
+#include "networkCommon.glsl"
+
+mat2
+getRot(float angle)
+{
+ return mat2(cos(angle), sin(angle), -sin(angle), cos(angle));
+}
+
+void
+main()
+{
+ const float angle = mix(c_angles[0][1], c_angles[0][0], gl_TessCoord.x);
+ rot = getRot(angle);
+ if (gl_TessCoord.x < startTolerance) {
+ pos = c_pos[0][1];
+ }
+ else if (gl_TessCoord.x > endTolerance) {
+ pos = c_pos[0][0];
+ }
+ else {
+ const int height = int(mix(c_pos[0][1].z, c_pos[0][0].z, gl_TessCoord.x));
+ pos = ivec3(c_cpos[0] + ivec2(rot * vec2(c_radius[0], 0)), height);
+ }
+
+ tpos = c_reps[0] * gl_TessCoord.x;
+ dist = viewPointDist(pos);
+}
diff --git a/gfx/gl/shaders/networkCurve.vert b/gfx/gl/shaders/networkCurve.vert
new file mode 100644
index 0000000..7e363d3
--- /dev/null
+++ b/gfx/gl/shaders/networkCurve.vert
@@ -0,0 +1,23 @@
+#version 460 core
+
+layout(location = 0) in ivec3 v_pos[2];
+layout(location = 2) in ivec3 v_centre;
+layout(location = 3) in float v_reps;
+layout(location = 4) in float v_angles[2];
+layout(location = 6) in float v_radius;
+
+flat out ivec3 pos[2];
+flat out ivec2 cpos;
+flat out float reps;
+flat out float angles[2];
+flat out float radius;
+
+void
+main()
+{
+ pos = v_pos;
+ cpos = v_centre.xy;
+ reps = v_reps;
+ angles = v_angles;
+ radius = v_radius;
+}
diff --git a/gfx/gl/shaders/networkCurve.vs b/gfx/gl/shaders/networkCurve.vs
deleted file mode 100644
index f51bb87..0000000
--- a/gfx/gl/shaders/networkCurve.vs
+++ /dev/null
@@ -1,29 +0,0 @@
-#version 330 core
-
-layout(location = 0) in ivec3 v_apos;
-layout(location = 1) in ivec3 v_bpos;
-layout(location = 2) in ivec3 v_centre;
-layout(location = 3) in float v_reps;
-layout(location = 4) in float v_aangle;
-layout(location = 5) in float v_bangle;
-layout(location = 6) in float v_radius;
-
-flat out ivec3 apos;
-flat out ivec3 bpos;
-flat out ivec3 cpos;
-flat out float reps;
-flat out float aangle;
-flat out float bangle;
-flat out float radius;
-
-void
-main()
-{
- apos = v_apos;
- bpos = v_bpos;
- cpos = v_centre;
- reps = v_reps;
- aangle = v_aangle;
- bangle = v_bangle;
- radius = v_radius;
-}
diff --git a/gfx/gl/shaders/networkStraight.geom b/gfx/gl/shaders/networkStraight.geom
new file mode 100644
index 0000000..0aa029a
--- /dev/null
+++ b/gfx/gl/shaders/networkStraight.geom
@@ -0,0 +1,18 @@
+#version 460 core
+#extension GL_ARB_shading_language_include : enable
+
+layout(points) in;
+layout(triangle_strip, max_vertices = 10) out;
+
+flat in ivec3 pos[][2];
+flat in mat2 rot[];
+flat in float reps[];
+flat in float dist[];
+
+#include "networkCommon.glsl"
+
+void
+main()
+{
+ doSeg(dist[0], pos[0][0], pos[0][1], 0.f, reps[0], rot[0], rot[0]);
+}
diff --git a/gfx/gl/shaders/networkStraight.gs b/gfx/gl/shaders/networkStraight.gs
deleted file mode 100644
index 51df5fb..0000000
--- a/gfx/gl/shaders/networkStraight.gs
+++ /dev/null
@@ -1,17 +0,0 @@
-#version 330 core
-
-flat in ivec3 apos[];
-flat in ivec3 bpos[];
-flat in mat2 rot[];
-flat in float reps[];
-flat in float dist[];
-
-layout(points) in;
-layout(triangle_strip, max_vertices = 10) out;
-include(`networkCommon.glsl')
-
-void
-main()
-{
- doSeg(dist[0], apos[0], bpos[0], 0.f, reps[0], rot[0], rot[0]);
-}
diff --git a/gfx/gl/shaders/networkStraight.vert b/gfx/gl/shaders/networkStraight.vert
new file mode 100644
index 0000000..ffd7deb
--- /dev/null
+++ b/gfx/gl/shaders/networkStraight.vert
@@ -0,0 +1,22 @@
+#version 460 core
+#extension GL_ARB_shading_language_include : enable
+
+layout(location = 0) in ivec3 v_pos[2];
+layout(location = 2) in mat2 v_rot;
+layout(location = 4) in float v_reps;
+
+flat out ivec3 pos[2];
+flat out mat2 rot;
+flat out float reps;
+flat out float dist;
+
+#include "networkCommon.glsl"
+
+void
+main()
+{
+ pos = v_pos;
+ rot = v_rot;
+ reps = v_reps;
+ dist = segDist(v_pos[0], v_pos[1]);
+}
diff --git a/gfx/gl/shaders/networkStraight.vs b/gfx/gl/shaders/networkStraight.vs
deleted file mode 100644
index 55f9c4f..0000000
--- a/gfx/gl/shaders/networkStraight.vs
+++ /dev/null
@@ -1,24 +0,0 @@
-#version 330 core
-
-layout(location = 0) in ivec3 v_apos;
-layout(location = 1) in ivec3 v_bpos;
-layout(location = 2) in mat2 v_rot;
-layout(location = 4) in float v_reps;
-
-flat out ivec3 apos;
-flat out ivec3 bpos;
-flat out mat2 rot;
-flat out float reps;
-flat out float dist;
-
-include(`networkCommon.glsl')
-
-void
-main()
-{
- apos = v_apos;
- bpos = v_bpos;
- rot = v_rot;
- reps = v_reps;
- dist = segDist(v_apos, v_bpos);
-}
diff --git a/gfx/gl/shaders/pointLight.fs b/gfx/gl/shaders/pointLight.frag
index 7531d3e..a141592 100644
--- a/gfx/gl/shaders/pointLight.fs
+++ b/gfx/gl/shaders/pointLight.frag
@@ -1,9 +1,8 @@
-#version 330 core
-#extension GL_ARB_shading_language_420pack : enable
+#version 460 core
out vec3 FragColor;
-layout(binding = 0) uniform isampler2D gPosition;
+layout(binding = 0) uniform sampler2D gPosition;
layout(binding = 1) uniform sampler2D gNormal;
uniform ivec4 viewPort;
flat in vec4 geo_centre;
diff --git a/gfx/gl/shaders/pointLight.gs b/gfx/gl/shaders/pointLight.geom
index fc1d7c3..1ee7e06 100644
--- a/gfx/gl/shaders/pointLight.gs
+++ b/gfx/gl/shaders/pointLight.geom
@@ -1,6 +1,4 @@
-#version 330 core
-#extension GL_ARB_enhanced_layouts : enable
-#extension GL_ARB_shading_language_420pack : enable
+#version 460 core
const vec3[] cube = vec3[]( // http://www.cs.umd.edu/gvil/papers/av_ts.pdf
vec3(-1, 1, 1), // Front-top-left
diff --git a/gfx/gl/shaders/pointLight.vert b/gfx/gl/shaders/pointLight.vert
new file mode 100644
index 0000000..61953ad
--- /dev/null
+++ b/gfx/gl/shaders/pointLight.vert
@@ -0,0 +1,28 @@
+#version 460 core
+#extension GL_ARB_shading_language_include : enable
+
+#include "commonLocationData.glsl"
+
+layout(location = 0) in vec3 v_position;
+layout(location = 1) in vec3 v_colour;
+layout(location = 2) in float v_kq;
+layout(location = 3) in uint index;
+mat3 model = mat3(locations[cldIndex[index]].rotationMatrix);
+ivec3 modelPos = locations[cldIndex[index]].position.xyz;
+
+uniform ivec3 viewPoint;
+
+flat out vec3 position;
+flat out vec3 colour;
+flat out float size;
+flat out float kq;
+
+void
+main()
+{
+ position = (modelPos - viewPoint) + ivec3(mat3(model) * v_position);
+ kq = v_kq;
+ size = (8000 * sqrt(max(max(v_colour.r, v_colour.g), v_colour.b))) / sqrt(v_kq);
+ colour = v_colour;
+ gl_Position = vec4(position, 0);
+}
diff --git a/gfx/gl/shaders/pointLight.vs b/gfx/gl/shaders/pointLight.vs
deleted file mode 100644
index fbd031c..0000000
--- a/gfx/gl/shaders/pointLight.vs
+++ /dev/null
@@ -1,24 +0,0 @@
-#version 330 core
-
-layout(location = 0) in vec3 v_position;
-layout(location = 1) in vec3 v_colour;
-layout(location = 2) in float v_kq;
-layout(location = 3) in mat3 model;
-layout(location = 6) in ivec3 modelPos;
-
-uniform ivec3 viewPoint;
-
-flat out vec3 position;
-flat out vec3 colour;
-flat out float size;
-flat out float kq;
-
-void
-main()
-{
- position = modelPos + ivec3(mat3(model) * v_position);
- kq = v_kq;
- size = (8000 * sqrt(max(max(v_colour.r, v_colour.g), v_colour.b))) / sqrt(v_kq);
- colour = v_colour;
- gl_Position = vec4(position - viewPoint, 0);
-}
diff --git a/gfx/gl/shaders/shadowDynamicPoint.vert b/gfx/gl/shaders/shadowDynamicPoint.vert
new file mode 100644
index 0000000..248b7b0
--- /dev/null
+++ b/gfx/gl/shaders/shadowDynamicPoint.vert
@@ -0,0 +1,10 @@
+#version 460 core
+#extension GL_ARB_shading_language_include : enable
+
+#include "meshIn.glsl"
+
+uniform ivec3 viewPoint;
+uniform mat3 model;
+uniform ivec3 modelPos;
+
+#include "commonShadowPoint.glsl"
diff --git a/gfx/gl/shaders/shadowDynamicPoint.vs b/gfx/gl/shaders/shadowDynamicPoint.vs
deleted file mode 100644
index 7335b9a..0000000
--- a/gfx/gl/shaders/shadowDynamicPoint.vs
+++ /dev/null
@@ -1,9 +0,0 @@
-#version 330 core
-
-include(`meshIn.glsl')
-
-uniform ivec3 viewPoint;
-uniform mat3 model;
-uniform ivec3 modelPos;
-
-include(`commonShadowPoint.glsl')
diff --git a/gfx/gl/shaders/shadowDynamicPointInst.vert b/gfx/gl/shaders/shadowDynamicPointInst.vert
new file mode 100644
index 0000000..b978e4a
--- /dev/null
+++ b/gfx/gl/shaders/shadowDynamicPointInst.vert
@@ -0,0 +1,12 @@
+#version 460 core
+#extension GL_ARB_shading_language_include : enable
+
+#include "commonLocationData.glsl"
+#include "meshIn.glsl"
+
+uniform ivec3 viewPoint;
+layout(location = 5) in uint index;
+mat3 model = mat3(locations[cldIndex[index]].rotationMatrix);
+ivec3 modelPos = locations[cldIndex[index]].position.xyz;
+
+#include "commonShadowPoint.glsl"
diff --git a/gfx/gl/shaders/shadowDynamicPointInst.vs b/gfx/gl/shaders/shadowDynamicPointInst.vs
deleted file mode 100644
index d0eb649..0000000
--- a/gfx/gl/shaders/shadowDynamicPointInst.vs
+++ /dev/null
@@ -1,9 +0,0 @@
-#version 330 core
-
-include(`meshIn.glsl')
-
-uniform ivec3 viewPoint;
-layout(location = 5) in mat3 model;
-layout(location = 8) in ivec3 modelPos;
-
-include(`commonShadowPoint.glsl')
diff --git a/gfx/gl/shaders/shadowDynamicPointInstWithTextures.fs b/gfx/gl/shaders/shadowDynamicPointInstWithTextures.frag
index 47ce9c0..e9d83d2 100644
--- a/gfx/gl/shaders/shadowDynamicPointInstWithTextures.fs
+++ b/gfx/gl/shaders/shadowDynamicPointInstWithTextures.frag
@@ -1,10 +1,10 @@
-#version 330 core
-#extension GL_ARB_shading_language_420pack : enable
+#version 460 core
+#extension GL_ARB_shading_language_include : enable
layout(binding = 3) uniform sampler2D textureAlbedo;
-include(`materialInterface.glsl')
-include(`materialCommon.glsl')
+#include "materialCommon.glsl"
+#include "materialDetail.glsl"
in vec2 texCoord;
flat in MaterialDetail material;
diff --git a/gfx/gl/shaders/shadowDynamicPointInstWithTextures.geom b/gfx/gl/shaders/shadowDynamicPointInstWithTextures.geom
new file mode 100644
index 0000000..72e4075
--- /dev/null
+++ b/gfx/gl/shaders/shadowDynamicPointInstWithTextures.geom
@@ -0,0 +1,5 @@
+#version 460
+#extension GL_ARB_shading_language_include : enable
+#define TEXTURES
+
+#include "commonShadowPoint-geom.glsl"
diff --git a/gfx/gl/shaders/shadowDynamicPointInstWithTextures.gs b/gfx/gl/shaders/shadowDynamicPointInstWithTextures.gs
deleted file mode 100644
index e6e213e..0000000
--- a/gfx/gl/shaders/shadowDynamicPointInstWithTextures.gs
+++ /dev/null
@@ -1,3 +0,0 @@
-define(`TEXTURES', 1)
-
-include(`commonShadowPoint.gs')
diff --git a/gfx/gl/shaders/shadowDynamicPointInstWithTextures.vert b/gfx/gl/shaders/shadowDynamicPointInstWithTextures.vert
new file mode 100644
index 0000000..a5e8245
--- /dev/null
+++ b/gfx/gl/shaders/shadowDynamicPointInstWithTextures.vert
@@ -0,0 +1,17 @@
+#version 460 core
+#extension GL_ARB_shading_language_include : enable
+#define TEXTURES
+
+layout(binding = 4) uniform usampler2DRect materialData;
+
+#include "commonLocationData.glsl"
+#include "getMaterialDetail.glsl"
+#include "materialInterface.glsl"
+#include "meshIn.glsl"
+
+uniform ivec3 viewPoint;
+layout(location = 5) in uint index;
+mat3 model = mat3(locations[cldIndex[index]].rotationMatrix);
+ivec3 modelPos = locations[cldIndex[index]].position.xyz;
+
+#include "commonShadowPoint.glsl"
diff --git a/gfx/gl/shaders/shadowDynamicPointInstWithTextures.vs b/gfx/gl/shaders/shadowDynamicPointInstWithTextures.vs
deleted file mode 100644
index a76c87f..0000000
--- a/gfx/gl/shaders/shadowDynamicPointInstWithTextures.vs
+++ /dev/null
@@ -1,15 +0,0 @@
-#version 330 core
-#extension GL_ARB_shading_language_420pack : enable
-
-layout(binding = 4) uniform usampler2DRect materialData;
-
-define(`TEXTURES', 1)
-include(`materialInterface.glsl')
-include(`getMaterialDetail.glsl')
-include(`meshIn.glsl')
-
-uniform ivec3 viewPoint;
-layout(location = 5) in mat3 model;
-layout(location = 8) in ivec3 modelPos;
-
-include(`commonShadowPoint.glsl')
diff --git a/gfx/gl/shaders/shadowDynamicPointStencil.frag b/gfx/gl/shaders/shadowDynamicPointStencil.frag
new file mode 100644
index 0000000..d6b8a0e
--- /dev/null
+++ b/gfx/gl/shaders/shadowDynamicPointStencil.frag
@@ -0,0 +1,15 @@
+#version 460 core
+
+layout(binding = 0) uniform sampler2DArray stencilDepth;
+flat in vec3 scale;
+in vec3 texCoord;
+
+void
+main()
+{
+ float stDepth = texture(stencilDepth, texCoord).r;
+ if (stDepth >= 1) {
+ discard;
+ }
+ gl_FragDepth = gl_FragCoord.z + ((stDepth - 0.5) * scale.z);
+}
diff --git a/gfx/gl/shaders/shadowDynamicPointStencil.geom b/gfx/gl/shaders/shadowDynamicPointStencil.geom
new file mode 100644
index 0000000..df8be8d
--- /dev/null
+++ b/gfx/gl/shaders/shadowDynamicPointStencil.geom
@@ -0,0 +1,35 @@
+#version 460 core
+
+const vec2[] corners = vec2[4](vec2(-1, -1), vec2(-1, 1), vec2(1, -1), vec2(1, 1));
+const float tau = 6.28318531;
+
+uniform mat4 viewProjection[4];
+uniform int viewProjections;
+uniform vec3 sizes[4];
+uniform float size;
+
+in float vmodelYaw[];
+in ivec3 vworldPos[];
+
+flat out vec3 scale;
+out vec3 texCoord;
+
+layout(points) in;
+layout(triangle_strip, max_vertices = 16) out;
+
+void
+main()
+{
+ int viewAngle = int(round(4.0 + (vmodelYaw[0] / tau))) % 8;
+ for (gl_Layer = 0; gl_Layer < viewProjections; ++gl_Layer) {
+ scale = 2.0 * size / sizes[gl_Layer];
+ vec4 pos = viewProjection[gl_Layer] * vec4(vworldPos[0], 1);
+ for (int c = 0; c < corners.length(); ++c) {
+ gl_Position = pos + vec4(scale.xy * corners[c], 0, 0);
+ gl_Position.z = max(gl_Position.z, -1);
+ texCoord = vec3((corners[c] * 0.5) + 0.5, viewAngle);
+ EmitVertex();
+ }
+ EndPrimitive();
+ }
+}
diff --git a/gfx/gl/shaders/shadowDynamicPointStencil.vert b/gfx/gl/shaders/shadowDynamicPointStencil.vert
new file mode 100644
index 0000000..0a41143
--- /dev/null
+++ b/gfx/gl/shaders/shadowDynamicPointStencil.vert
@@ -0,0 +1,18 @@
+#version 460 core
+#extension GL_ARB_shading_language_include : enable
+
+#include "commonLocationData.glsl"
+
+layout(location = 0) in uint index;
+uniform ivec3 viewPoint;
+uniform vec3 centre;
+
+out float vmodelYaw;
+out ivec3 vworldPos;
+
+void
+main()
+{
+ vmodelYaw = locations[cldIndex[index]].rotation.x;
+ vworldPos = locations[cldIndex[index]].position.xyz - viewPoint + ivec3(centre);
+}
diff --git a/gfx/gl/shaders/shadowLandmass.vs b/gfx/gl/shaders/shadowLandmass.vert
index becf142..cf68fe5 100644
--- a/gfx/gl/shaders/shadowLandmass.vs
+++ b/gfx/gl/shaders/shadowLandmass.vert
@@ -1,4 +1,4 @@
-#version 330 core
+#version 460 core
layout(location = 0) in ivec3 position;
diff --git a/gfx/gl/shaders/shadowStencil.frag b/gfx/gl/shaders/shadowStencil.frag
new file mode 100644
index 0000000..35cdf6e
--- /dev/null
+++ b/gfx/gl/shaders/shadowStencil.frag
@@ -0,0 +1,19 @@
+#version 460 core
+#extension GL_ARB_shading_language_include : enable
+
+layout(binding = 0) uniform sampler2D textureAlbedo;
+
+#include "materialCommon.glsl"
+#include "materialDetail.glsl"
+
+in vec2 gTexCoords;
+flat in MaterialDetail gMaterial;
+
+void
+main()
+{
+ if (getTextureColour(gMaterial, gTexCoords).a < 0.5) {
+ discard;
+ }
+ gl_FragDepth = gl_FragCoord.z;
+}
diff --git a/gfx/gl/shaders/shadowStencil.geom b/gfx/gl/shaders/shadowStencil.geom
new file mode 100644
index 0000000..9c5ba48
--- /dev/null
+++ b/gfx/gl/shaders/shadowStencil.geom
@@ -0,0 +1,28 @@
+#version 460 core
+#extension GL_ARB_shading_language_include : enable
+
+#include "materialDetail.glsl"
+
+layout(triangles) in;
+layout(triangle_strip, max_vertices = 24) out;
+
+uniform mat4 viewProjection[8];
+in vec3 FragPos[];
+in vec2 TexCoords[];
+flat in MaterialDetail Material[];
+out vec2 gTexCoords;
+flat out MaterialDetail gMaterial;
+
+void
+main()
+{
+ for (gl_Layer = 0; gl_Layer < viewProjection.length(); ++gl_Layer) {
+ for (int v = 0; v < FragPos.length(); ++v) {
+ gl_Position = viewProjection[gl_Layer] * vec4(FragPos[v], 1);
+ gTexCoords = TexCoords[v];
+ gMaterial = Material[v];
+ EmitVertex();
+ }
+ EndPrimitive();
+ }
+}
diff --git a/gfx/gl/shaders/shadowStencil.vert b/gfx/gl/shaders/shadowStencil.vert
new file mode 100644
index 0000000..98e8434
--- /dev/null
+++ b/gfx/gl/shaders/shadowStencil.vert
@@ -0,0 +1,20 @@
+#version 460 core
+#extension GL_ARB_shading_language_include : enable
+
+layout(binding = 1) uniform usampler2DRect materialData;
+
+#include "getMaterialDetail.glsl"
+#include "materialDetail.glsl"
+#include "meshIn.glsl"
+
+out vec3 FragPos;
+out vec2 TexCoords;
+flat out MaterialDetail Material;
+
+void
+main()
+{
+ TexCoords = texCoord;
+ Material = getMaterialDetail(material);
+ FragPos = position;
+}
diff --git a/gfx/gl/shaders/spotLight.fs b/gfx/gl/shaders/spotLight.frag
index ad33458..0241e88 100644
--- a/gfx/gl/shaders/spotLight.fs
+++ b/gfx/gl/shaders/spotLight.frag
@@ -1,9 +1,8 @@
-#version 330 core
-#extension GL_ARB_shading_language_420pack : enable
+#version 460 core
out vec3 FragColor;
-layout(binding = 0) uniform isampler2D gPosition;
+layout(binding = 0) uniform sampler2D gPosition;
layout(binding = 1) uniform sampler2D gNormal;
uniform ivec4 viewPort;
flat in vec4 geo_centre;
diff --git a/gfx/gl/shaders/spotLight.gs b/gfx/gl/shaders/spotLight.geom
index 194812a..fec191e 100644
--- a/gfx/gl/shaders/spotLight.gs
+++ b/gfx/gl/shaders/spotLight.geom
@@ -1,6 +1,4 @@
-#version 330 core
-#extension GL_ARB_enhanced_layouts : enable
-#extension GL_ARB_shading_language_420pack : enable
+#version 460 core
const vec3[] pyramid = vec3[]( // four-sided
vec3(0, 0, 0), // Apex
diff --git a/gfx/gl/shaders/spotLight.vs b/gfx/gl/shaders/spotLight.vert
index e0196c3..6f72a63 100644
--- a/gfx/gl/shaders/spotLight.vs
+++ b/gfx/gl/shaders/spotLight.vert
@@ -1,12 +1,16 @@
-#version 330 core
+#version 460 core
+#extension GL_ARB_shading_language_include : enable
+
+#include "commonLocationData.glsl"
layout(location = 0) in vec3 v_position;
layout(location = 1) in vec3 v_direction;
layout(location = 2) in vec3 v_colour;
layout(location = 3) in float v_kq;
layout(location = 4) in float v_arc;
-layout(location = 5) in mat3 model;
-layout(location = 8) in ivec3 modelPos;
+layout(location = 5) in uint index;
+mat3 model = mat3(locations[cldIndex[index]].rotationMatrix);
+ivec3 modelPos = locations[cldIndex[index]].position.xyz;
uniform ivec3 viewPoint;
@@ -20,11 +24,11 @@ flat out float kq;
void
main()
{
- position = modelPos + ivec3(mat3(model) * v_position);
+ position = (modelPos - viewPoint) + ivec3(mat3(model) * v_position);
direction = normalize(mat3(model) * v_direction);
colour = v_colour;
kq = v_kq;
size = (8000 * sqrt(max(max(colour.r, colour.g), colour.b))) / sqrt(kq);
arc = vec2(cos(v_arc / 2), tan(v_arc / 2));
- gl_Position = vec4(position - viewPoint, 0);
+ gl_Position = vec4(position, 0);
}
diff --git a/gfx/gl/shaders/uiShader.fs b/gfx/gl/shaders/uiShader.fs
deleted file mode 100644
index c5f4e92..0000000
--- a/gfx/gl/shaders/uiShader.fs
+++ /dev/null
@@ -1,11 +0,0 @@
-#version 330 core
-
-in vec2 texCoord0;
-
-uniform sampler2D sampler;
-
-void
-main()
-{
- gl_FragColor = texture(sampler, texCoord0);
-}
diff --git a/gfx/gl/shaders/uiShader.vs b/gfx/gl/shaders/uiShader.vs
deleted file mode 100644
index e9e4373..0000000
--- a/gfx/gl/shaders/uiShader.vs
+++ /dev/null
@@ -1,13 +0,0 @@
-#version 330 core
-
-in vec4 position;
-
-out vec2 texCoord0;
-uniform mat4 uiProjection;
-
-void
-main()
-{
- gl_Position = uiProjection * vec4(position.xy, 0.0, 1.0);
- texCoord0 = position.zw;
-}
diff --git a/gfx/gl/shaders/uiShaderFont.fs b/gfx/gl/shaders/uiShaderFont.fs
deleted file mode 100644
index a1ef6ef..0000000
--- a/gfx/gl/shaders/uiShaderFont.fs
+++ /dev/null
@@ -1,12 +0,0 @@
-#version 330 core
-
-in vec2 texCoord0;
-
-uniform sampler2D sampler;
-uniform vec3 colour;
-
-void
-main()
-{
- gl_FragColor = vec4(colour, texture(sampler, texCoord0).r);
-}
diff --git a/gfx/gl/shaders/water.frag b/gfx/gl/shaders/water.frag
new file mode 100644
index 0000000..0c57bfb
--- /dev/null
+++ b/gfx/gl/shaders/water.frag
@@ -0,0 +1,18 @@
+#version 460 core
+#extension GL_ARB_shading_language_include : enable
+
+#include "materialOut.glsl"
+
+in vec4 FragPos;
+in vec2 TexCoords;
+
+uniform sampler2D texture0;
+
+void
+main()
+{
+ gPosition = vec4(FragPos.xyz, 1);
+ gNormal = vec4(0, 0, 1, 1);
+ gAlbedoSpec = texture(texture0, TexCoords);
+ gAlbedoSpec.a *= clamp(-FragPos.w * .0007, .1, 1.0);
+}
diff --git a/gfx/gl/shaders/water.fs b/gfx/gl/shaders/water.fs
deleted file mode 100644
index 0918d9f..0000000
--- a/gfx/gl/shaders/water.fs
+++ /dev/null
@@ -1,17 +0,0 @@
-#version 330 core
-#extension GL_ARB_shading_language_420pack : enable
-
-in vec3 FragPos;
-in vec2 TexCoords;
-include(`materialOut.glsl')
-
-uniform sampler2D texture0;
-
-void
-main()
-{
- gPosition = ivec4(FragPos, 1);
- gNormal = vec4(0, 0, 1, 1);
- gAlbedoSpec = texture(texture0, TexCoords);
- gAlbedoSpec.a *= clamp(-FragPos.z * .0007, .1, 1.0);
-}
diff --git a/gfx/gl/shaders/water.vs b/gfx/gl/shaders/water.vert
index 58bf7b6..bb056b8 100644
--- a/gfx/gl/shaders/water.vs
+++ b/gfx/gl/shaders/water.vert
@@ -1,7 +1,7 @@
-#version 330 core
+#version 460 core
layout(location = 0) in ivec3 position;
-out vec3 FragPos;
+out vec4 FragPos;
out vec2 TexCoords;
uniform mat4 viewProjection;
@@ -14,8 +14,8 @@ main()
vec3 wpos = vec3(position.x + (cos(waves) * 1000.0), position.y + (cos(waves * 1.4) * 1000.0),
cos(waves + (position.x / 1000000) + (position.y / 8000)) * 300.0);
- FragPos = vec3(wpos.xy, position.z);
+ FragPos = vec4(wpos - viewPoint, position.z);
TexCoords = (position.xy / 8192) - (viewPoint.xy / 8192);
- gl_Position = viewProjection * vec4(wpos - viewPoint, 1.0);
+ gl_Position = viewProjection * vec4(FragPos.xyz, 1.0);
}
diff --git a/gfx/gl/shadowMapper.cpp b/gfx/gl/shadowMapper.cpp
index a846a3d..e01fd20 100644
--- a/gfx/gl/shadowMapper.cpp
+++ b/gfx/gl/shadowMapper.cpp
@@ -1,114 +1,138 @@
#include "shadowMapper.h"
-#include "camera.h"
#include "collections.h"
-#include "gfx/gl/shaders/fs-shadowDynamicPointInstWithTextures.h"
-#include "gfx/gl/shaders/gs-commonShadowPoint.h"
-#include "gfx/gl/shaders/gs-shadowDynamicPointInstWithTextures.h"
-#include "gfx/gl/shaders/vs-shadowDynamicPoint.h"
-#include "gfx/gl/shaders/vs-shadowDynamicPointInst.h"
-#include "gfx/gl/shaders/vs-shadowDynamicPointInstWithTextures.h"
-#include "gfx/gl/shaders/vs-shadowLandmass.h"
+#include "game/gamestate.h"
+#include "gfx/aabb.h"
+#include "gfx/gl/shadowStenciller.h"
+#include "gfx/lightDirection.h"
+#include "gfx/renderable.h"
#include "gl_traits.h"
+#include "gldebug.h"
#include "location.h"
-#include "maths.h"
#include "sceneProvider.h"
#include "sceneShader.h"
-#include "sorting.h"
+#include <gfx/camera.h>
+#include <gfx/gl/shaders/commonShadowPoint-geom.h>
+#include <gfx/gl/shaders/shadowDynamicPoint-vert.h>
+#include <gfx/gl/shaders/shadowDynamicPointInst-vert.h>
+#include <gfx/gl/shaders/shadowDynamicPointInstWithTextures-frag.h>
+#include <gfx/gl/shaders/shadowDynamicPointInstWithTextures-geom.h>
+#include <gfx/gl/shaders/shadowDynamicPointInstWithTextures-vert.h>
+#include <gfx/gl/shaders/shadowDynamicPointStencil-frag.h>
+#include <gfx/gl/shaders/shadowDynamicPointStencil-geom.h>
+#include <gfx/gl/shaders/shadowDynamicPointStencil-vert.h>
+#include <gfx/gl/shaders/shadowLandmass-vert.h>
#include <glm/gtc/type_ptr.hpp>
#include <glm/gtx/transform.hpp>
#include <glm/matrix.hpp>
-#include <vector>
+#include <maths.h>
ShadowMapper::ShadowMapper(const TextureAbsCoord & s) :
- landmess {shadowLandmass_vs}, dynamicPointInst {shadowDynamicPointInst_vs},
- dynamicPointInstWithTextures {shadowDynamicPointInstWithTextures_vs, shadowDynamicPointInstWithTextures_gs,
- shadowDynamicPointInstWithTextures_fs},
- size {s}
+ landmess {shadowLandmass_vert}, dynamicPointInst {shadowDynamicPointInst_vert},
+ dynamicPointInstWithTextures {shadowDynamicPointInstWithTextures_vert, shadowDynamicPointInstWithTextures_geom,
+ shadowDynamicPointInstWithTextures_frag},
+ size {s}, frustum {{}, {}, {}}
{
- glBindTexture(GL_TEXTURE_2D_ARRAY, depthMap);
- glTexImage3D(
- GL_TEXTURE_2D_ARRAY, 0, GL_DEPTH_COMPONENT, size.x, size.y, 4, 0, GL_DEPTH_COMPONENT, GL_FLOAT, nullptr);
- glTexParameter(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
- glTexParameter(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
- glTexParameter(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
- glTexParameter(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
+ glDebugScope _ {depthMap};
+ depthMap.storage(1, GL_DEPTH_COMPONENT16, size || static_cast<GLsizei>(SHADOW_BANDS));
+ depthMap.parameter(GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ depthMap.parameter(GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ depthMap.parameter(GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
+ depthMap.parameter(GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
static constexpr RGBA border {std::numeric_limits<RGBA::value_type>::infinity()};
- glTexParameter(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_BORDER_COLOR, border);
+ depthMap.parameter(GL_TEXTURE_BORDER_COLOR, border);
- glBindFramebuffer(GL_FRAMEBUFFER, depthMapFBO);
- glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, depthMap, 0);
- glDrawBuffer(GL_NONE);
- glReadBuffer(GL_NONE);
- if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
- throw std::runtime_error("Framebuffer not complete!");
- }
- glBindFramebuffer(GL_FRAMEBUFFER, 0);
+ depthMapFBO.texture(GL_DEPTH_ATTACHMENT, depthMap);
+ depthMapFBO.drawBuffers(GL_NONE);
+ depthMapFBO.assertComplete();
}
-constexpr std::array<GlobalDistance, ShadowMapper::SHADOW_BANDS + 1> shadowBands {
- 1000,
- 250000,
- 750000,
- 2500000,
- 10000000,
-};
+constexpr GlobalDistance SHADOW_NEAR = 1;
+constexpr GlobalDistance SHADOW_FAR = 10'000'000;
+constexpr auto SHADOW_BANDS_DISTS
+ = []<GlobalDistance... Ints>(const float scaleFactor, std::integer_sequence<GlobalDistance, Ints...>) {
+ const auto base = SHADOW_FAR / pow(scaleFactor, sizeof...(Ints) - 1);
+ return std::array {SHADOW_NEAR, static_cast<GlobalDistance>((base * pow(scaleFactor, Ints)))...};
+ }(4.6F, std::make_integer_sequence<GlobalDistance, ShadowMapper::SHADOW_BANDS>());
-std::vector<std::array<RelativePosition3D, 4>>
-ShadowMapper::getBandViewExtents(const Camera & camera, const glm::mat4 & lightViewDir)
+static_assert(SHADOW_BANDS_DISTS.front() == 1);
+static_assert(SHADOW_BANDS_DISTS.back() == SHADOW_FAR);
+static_assert(SHADOW_BANDS_DISTS.size() == ShadowMapper::SHADOW_BANDS + 1);
+
+size_t
+ShadowMapper::getBandViewExtents(
+ BandViewExtents & bandViewExtents, const Camera & camera, const glm::mat4 & lightViewDir)
{
- std::vector<std::array<RelativePosition3D, 4>> bandViewExtents;
- for (const auto dist : shadowBands) {
+ size_t band = 0;
+ for (const auto dist : SHADOW_BANDS_DISTS) {
const auto extents = camera.extentsAtDist(dist);
- bandViewExtents.emplace_back(extents * [&lightViewDir, cameraPos = camera.getPosition()](const auto & e) {
- return glm::mat3(lightViewDir) * (e.xyz() - cameraPos);
- });
- if (std::none_of(extents.begin(), extents.end(), [targetDist = dist - 1](const auto & e) {
- return e.w > targetDist;
+ bandViewExtents[band++] = extents * [&lightViewDir, cameraPos = camera.getPosition()](const auto & extent) {
+ return glm::mat3(lightViewDir) * (extent.xyz() - cameraPos);
+ };
+ if (std::ranges::none_of(extents, [dist](const auto & extent) {
+ return extent.w >= dist;
})) {
break;
}
}
- return bandViewExtents;
+ return band;
}
-ShadowMapper::Definitions
-ShadowMapper::update(const SceneProvider & scene, const Direction3D & dir, const Camera & camera) const
+const Frustum &
+ShadowMapper::preFrame(const LightDirection & dir, const Camera & camera)
{
+ const auto lightViewDir = glm::lookAt({}, dir.vector(), Camera::upFromForward(dir.vector()));
+ const auto lightViewPoint = camera.getPosition();
+ const auto bandViewExtentCount = getBandViewExtents(bandViewExtents, camera, lightViewDir);
+ const auto activeBandViewExtents = std::span(bandViewExtents).subspan(0, bandViewExtentCount);
+
+ using ExtentsBoundingBox = AxisAlignedBoundingBox<RelativeDistance>;
+ for (auto out = std::make_pair(sizes.begin(), definitions.begin());
+ const auto & [near, far] : activeBandViewExtents | std::views::pairwise) {
+ const auto extents = ExtentsBoundingBox::fromPoints(std::span {near.begin(), far.end()});
+ const auto lightProjection = glm::ortho(
+ extents.min.x, extents.max.x, extents.min.y, extents.max.y, -extents.max.z, -extents.min.z);
+ *out.first++ = extents.max - extents.min;
+ *out.second++ = lightProjection * lightViewDir;
+ }
+
+ const auto extents = ExtentsBoundingBox::fromPoints(activeBandViewExtents.back()) += {};
+ const auto lightProjection
+ = glm::ortho(extents.min.x, extents.max.x, extents.min.y, extents.max.y, -extents.max.z, -extents.min.z);
+ frustum = {lightViewPoint, lightViewDir, lightProjection};
+ return frustum;
+}
+
+std::span<const glm::mat4>
+ShadowMapper::update(const SceneProvider & scene, const LightDirection & dir, const Camera & camera) const
+{
+ glDebugScope _ {depthMap};
+ glCullFace(GL_FRONT);
+ glEnable(GL_DEPTH_TEST);
+
+ shadowStenciller.setLightDirection(dir);
+ for (const auto & [id, asset] : gameState->assets) {
+ if (const auto r = asset.getAs<const Renderable>()) {
+ r->updateStencil(shadowStenciller);
+ }
+ }
+
glBindFramebuffer(GL_FRAMEBUFFER, depthMapFBO);
glClear(GL_DEPTH_BUFFER_BIT);
- glCullFace(GL_FRONT);
glViewport(0, 0, size.x, size.y);
- const auto lightViewDir = glm::lookAt({}, dir, up);
const auto lightViewPoint = camera.getPosition();
- const auto bandViewExtents = getBandViewExtents(camera, lightViewDir);
- Definitions out;
- std::transform(bandViewExtents.begin(), std::prev(bandViewExtents.end()), std::next(bandViewExtents.begin()),
- std::back_inserter(out),
- [bands = bandViewExtents.size() - 2, &lightViewDir](const auto & near, const auto & far) mutable {
- const auto extents_minmax = [extents = std::span {near.begin(), far.end()}](auto && comp) {
- const auto mm = std::minmax_element(extents.begin(), extents.end(), comp);
- return std::make_pair(comp.get(*mm.first), comp.get(*mm.second));
- };
-
- 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_minmax(CompareBy {0}), extents_minmax(CompareBy {1}), extents_minmax(CompareBy {2}));
-
- return lightProjection * lightViewDir;
- });
for (const auto p : std::initializer_list<const ShadowProgram *> {
- &landmess, &dynamicPoint, &dynamicPointInst, &dynamicPointInstWithTextures}) {
- p->setView(out, lightViewPoint);
+ &landmess, &dynamicPoint, &dynamicPointInst, &dynamicPointInstWithTextures, &stencilShadowProgram}) {
+ p->setView(definitions, sizes, lightViewPoint);
}
- scene.shadows(*this);
+ scene.shadows(*this, frustum);
glCullFace(GL_BACK);
- return out;
+ return definitions;
}
-ShadowMapper::ShadowProgram::ShadowProgram(const Shader & vs) : Program {vs, commonShadowPoint_gs} { }
+ShadowMapper::ShadowProgram::ShadowProgram(const Shader & vs) : Program {vs, commonShadowPoint_geom} { }
ShadowMapper::ShadowProgram::ShadowProgram(const Shader & vs, const Shader & gs, const Shader & fs) :
Program {vs, gs, fs}
@@ -116,12 +140,15 @@ ShadowMapper::ShadowProgram::ShadowProgram(const Shader & vs, const Shader & gs,
}
void
-ShadowMapper::ShadowProgram::setView(
- const std::span<const glm::mat4> viewProjection, const GlobalPosition3D viewPoint) const
+ShadowMapper::ShadowProgram::setView(const std::span<const glm::mat4x4> viewProjection,
+ const std::span<const RelativePosition3D> sizes, const GlobalPosition3D viewPoint) const
{
use();
glUniform(viewPointLoc, viewPoint);
glUniform(viewProjectionLoc, viewProjection);
+ if (sizesLoc) {
+ glUniform(sizesLoc, sizes);
+ }
glUniform(viewProjectionsLoc, static_cast<GLint>(viewProjection.size()));
}
@@ -131,7 +158,7 @@ ShadowMapper::ShadowProgram::use() const
glUseProgram(*this);
}
-ShadowMapper::DynamicPoint::DynamicPoint() : ShadowProgram {shadowDynamicPoint_vs} { }
+ShadowMapper::DynamicPoint::DynamicPoint() : ShadowProgram {shadowDynamicPoint_vert} { }
void
ShadowMapper::DynamicPoint::use(const Location & location) const
@@ -146,3 +173,16 @@ ShadowMapper::DynamicPoint::setModel(const Location & location) const
glUniform(modelLoc, location.getRotationTransform());
glUniform(modelPosLoc, location.pos);
}
+
+ShadowMapper::StencilShadowProgram::StencilShadowProgram() :
+ ShadowProgram {shadowDynamicPointStencil_vert, shadowDynamicPointStencil_geom, shadowDynamicPointStencil_frag}
+{
+}
+
+void
+ShadowMapper::StencilShadowProgram::use(const RelativePosition3D & centre, const float size) const
+{
+ Program::use();
+ glUniform(centreLoc, centre);
+ glUniform(sizeLoc, size);
+}
diff --git a/gfx/gl/shadowMapper.h b/gfx/gl/shadowMapper.h
index 73dadd0..f0356b9 100644
--- a/gfx/gl/shadowMapper.h
+++ b/gfx/gl/shadowMapper.h
@@ -1,15 +1,18 @@
#pragma once
#include "config/types.h"
+#include "gfx/frustum.h"
+#include "gfx/gl/shadowStenciller.h"
#include "lib/glArrays.h"
#include "program.h"
+#include <array>
#include <gfx/models/texture.h>
#include <glm/vec2.hpp>
#include <span>
-#include <vector>
class SceneProvider;
class Camera;
+class LightDirection;
class ShadowMapper {
public:
@@ -17,21 +20,26 @@ public:
static constexpr std::size_t SHADOW_BANDS {4};
- using Definitions = std::vector<glm::mat4x4>;
+ using Definitions = std::array<glm::mat4, SHADOW_BANDS>;
+ using Sizes = std::array<RelativePosition3D, SHADOW_BANDS>;
- [[nodiscard]] Definitions update(const SceneProvider &, const Direction3D & direction, const Camera &) const;
+ const Frustum & preFrame(const LightDirection & direction, const Camera &);
+ [[nodiscard]] std::span<const glm::mat4> update(
+ const SceneProvider &, const LightDirection & direction, const Camera &) const;
class ShadowProgram : public Program {
public:
explicit ShadowProgram(const Shader & vs);
explicit ShadowProgram(const Shader & vs, const Shader & gs, const Shader & fs);
- void setView(const std::span<const glm::mat4>, const GlobalPosition3D) const;
+ void setView(const std::span<const glm::mat4x4>, const std::span<const RelativePosition3D>,
+ const GlobalPosition3D) const;
void use() const;
private:
RequiredUniformLocation viewProjectionLoc {*this, "viewProjection"};
RequiredUniformLocation viewProjectionsLoc {*this, "viewProjections"};
+ UniformLocation sizesLoc {*this, "sizes"};
RequiredUniformLocation viewPointLoc {*this, "viewPoint"};
};
@@ -46,19 +54,37 @@ public:
RequiredUniformLocation modelPosLoc {*this, "modelPos"};
};
+ class StencilShadowProgram : public ShadowProgram {
+ public:
+ StencilShadowProgram();
+ void use(const RelativePosition3D & centre, const float size) const;
+
+ private:
+ RequiredUniformLocation centreLoc {*this, "centre"};
+ RequiredUniformLocation sizeLoc {*this, "size"};
+ };
+
ShadowProgram landmess, dynamicPointInst, dynamicPointInstWithTextures;
DynamicPoint dynamicPoint;
+ StencilShadowProgram stencilShadowProgram;
- // NOLINTNEXTLINE(hicpp-explicit-conversions)
- operator GLuint() const
+ void
+ bind(GLenum unit) const
{
- return depthMap;
+ depthMap.bind(unit);
}
private:
- [[nodiscard]] static std::vector<std::array<RelativePosition3D, 4>> getBandViewExtents(
- const Camera &, const glm::mat4 & lightView);
- glFrameBuffer depthMapFBO;
- glTexture depthMap;
+ using BandViewExtents = std::array<std::array<RelativePosition3D, 4>, SHADOW_BANDS + 1>;
+ [[nodiscard]] static size_t getBandViewExtents(BandViewExtents &, const Camera &, const glm::mat4 & lightView);
+ glFramebuffer depthMapFBO;
+ glTexture<GL_TEXTURE_2D_ARRAY> depthMap;
TextureAbsCoord size;
+
+ BandViewExtents bandViewExtents;
+ Definitions definitions;
+ Sizes sizes;
+ Frustum frustum;
+
+ mutable ShadowStenciller shadowStenciller;
};
diff --git a/gfx/gl/shadowStenciller.cpp b/gfx/gl/shadowStenciller.cpp
new file mode 100644
index 0000000..77ce309
--- /dev/null
+++ b/gfx/gl/shadowStenciller.cpp
@@ -0,0 +1,75 @@
+#include "shadowStenciller.h"
+#include "gfx/lightDirection.h"
+#include "gfx/models/mesh.h"
+#include "gl_traits.h"
+#include "gldebug.h"
+#include "maths.h"
+#include <gfx/gl/shaders/shadowStencil-frag.h>
+#include <gfx/gl/shaders/shadowStencil-geom.h>
+#include <gfx/gl/shaders/shadowStencil-vert.h>
+#include <stdexcept>
+
+ShadowStenciller::ShadowStenciller() :
+ shadowCaster {shadowStencil_vert, shadowStencil_geom, shadowStencil_frag}, lightDir {}, viewProjections {}
+{
+ glDebugScope _ {fbo};
+ glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+ glDrawBuffer(GL_NONE);
+ glReadBuffer(GL_NONE);
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+}
+
+void
+ShadowStenciller::setLightDirection(const LightDirection & lightDir)
+{
+ this->lightDir = lightDir.position();
+ viewProjections = [&lightDir]<GLint... Ep>(std::integer_sequence<GLint, Ep...>) {
+ constexpr float STEP = two_pi / STENCIL_ANGLES<decltype(two_pi)>;
+ return std::array {rotate_pitch<4>(half_pi - lightDir.position().y)
+ * rotate_yaw<4>((Ep * STEP) - lightDir.position().x)...};
+ }(std::make_integer_sequence<GLint, STENCIL_ANGLES<GLint>>());
+}
+
+Direction2D
+ShadowStenciller::getLightDirection() const
+{
+ return lightDir;
+}
+
+void
+ShadowStenciller::configureStencilTexture(glTexture<GL_TEXTURE_2D_ARRAY> & stencil, ImageDimensions size)
+{
+ glDebugScope _ {0};
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+ stencil.storage(1, GL_DEPTH_COMPONENT16, size || STENCIL_ANGLES<GLsizei>);
+ stencil.parameter(GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ stencil.parameter(GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ stencil.parameter(GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ stencil.parameter(GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+}
+
+void
+ShadowStenciller::renderStencil(
+ const glTexture<GL_TEXTURE_2D_ARRAY> & stencil, const MeshBase & mesh, const Texture::AnyPtr texture) const
+{
+ glDebugScope _ {fbo};
+ glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+ fbo.texture(GL_DEPTH_ATTACHMENT, stencil);
+ fbo.assertComplete();
+ if (texture) {
+ texture->bind(0);
+ }
+ glUseProgram(shadowCaster);
+ glClear(GL_DEPTH_BUFFER_BIT);
+ const auto stencilSize = stencil.getSize();
+ glViewport(0, 0, stencilSize.x, stencilSize.y);
+ const auto & centre = mesh.getDimensions().centre;
+ const auto & size = mesh.getDimensions().size;
+ glUniform(viewProjectionLoc,
+ std::span<const glm::mat4> {viewProjections *
+ [extentsMat = glm::translate(glm::ortho(-size, size, -size, size, -size, size), -centre)](
+ const auto & viewProjection) {
+ return viewProjection * extentsMat;
+ }});
+ mesh.draw();
+}
diff --git a/gfx/gl/shadowStenciller.h b/gfx/gl/shadowStenciller.h
new file mode 100644
index 0000000..ff88ff7
--- /dev/null
+++ b/gfx/gl/shadowStenciller.h
@@ -0,0 +1,28 @@
+#pragma once
+
+#include "gfx/models/mesh.h"
+#include "gfx/models/texture.h"
+#include "glFramebuffer.h"
+#include "program.h"
+
+class LightDirection;
+
+class ShadowStenciller {
+public:
+ template<typename T> static constexpr T STENCIL_ANGLES = 8;
+
+ ShadowStenciller();
+
+ static void configureStencilTexture(glTexture<GL_TEXTURE_2D_ARRAY> &, ImageDimensions);
+ void setLightDirection(const LightDirection & lightDir);
+ [[nodiscard]] Direction2D getLightDirection() const;
+ void renderStencil(const glTexture<GL_TEXTURE_2D_ARRAY> &, const MeshBase &, Texture::AnyPtr texture) const;
+
+private:
+ mutable glFramebuffer fbo;
+ Program shadowCaster;
+ Program::RequiredUniformLocation viewProjectionLoc {shadowCaster, "viewProjection"};
+
+ Direction2D lightDir;
+ std::array<glm::mat4, STENCIL_ANGLES<size_t>> viewProjections;
+};
diff --git a/gfx/gl/uiShader.cpp b/gfx/gl/uiShader.cpp
deleted file mode 100644
index 23da9dc..0000000
--- a/gfx/gl/uiShader.cpp
+++ /dev/null
@@ -1,27 +0,0 @@
-#include "uiShader.h"
-#include "gl_traits.h"
-#include <gfx/gl/program.h>
-#include <gfx/gl/shader.h>
-#include <gfx/gl/shaders/fs-uiShader.h>
-#include <gfx/gl/shaders/fs-uiShaderFont.h>
-#include <gfx/gl/shaders/vs-uiShader.h>
-#include <glm/glm.hpp>
-#include <glm/gtc/type_ptr.hpp>
-
-UIShader::IconProgram::IconProgram(const glm::mat4 & vp) : UIProgram {vp, uiShader_vs, uiShader_fs} { }
-
-UIShader::TextProgram::TextProgram(const glm::mat4 & vp) : UIProgram {vp, uiShader_vs, uiShaderFont_fs} { }
-
-UIShader::UIShader(size_t width, size_t height) :
- UIShader {glm::ortho<float>(0, static_cast<float>(width), 0, static_cast<float>(height))}
-{
-}
-
-UIShader::UIShader(const glm::mat4 & viewProjection) : icon {viewProjection}, text {viewProjection} { }
-
-void
-UIShader::TextProgram::use(const RGB & colour) const
-{
- Program::use();
- glUniform(colorLoc, colour);
-}
diff --git a/gfx/gl/uiShader.h b/gfx/gl/uiShader.h
deleted file mode 100644
index 6d00166..0000000
--- a/gfx/gl/uiShader.h
+++ /dev/null
@@ -1,47 +0,0 @@
-#pragma once
-
-#include "config/types.h"
-#include "gl_traits.h"
-#include "program.h"
-#include <cstddef>
-#include <glad/gl.h>
-#include <glm/glm.hpp>
-#include <glm/gtc/type_ptr.hpp>
-
-class UIShader {
-public:
- UIShader(std::size_t width, std::size_t height);
-
-private:
- explicit UIShader(const glm::mat4 & viewProjection);
-
- class UIProgram : public Program {
- public:
- template<typename... S>
- explicit UIProgram(const glm::mat4 & vp, S &&... srcs) : Program {std::forward<S>(srcs)...}
- {
- const RequiredUniformLocation uiProjectionLoc {*this, "uiProjection"};
- glUseProgram(*this);
- glUniform(uiProjectionLoc, vp);
- }
- };
-
- class IconProgram : public UIProgram {
- public:
- explicit IconProgram(const glm::mat4 & vp);
- using Program::use;
- };
-
- class TextProgram : public UIProgram {
- public:
- explicit TextProgram(const glm::mat4 & vp);
- void use(const RGB & colour) const;
-
- private:
- RequiredUniformLocation colorLoc {*this, "colour"};
- };
-
-public:
- IconProgram icon;
- TextProgram text;
-};
diff --git a/gfx/gl/vertexArrayObject.h b/gfx/gl/vertexArrayObject.h
deleted file mode 100644
index 57daaf3..0000000
--- a/gfx/gl/vertexArrayObject.h
+++ /dev/null
@@ -1,117 +0,0 @@
-#pragma once
-
-#include "collections.h"
-#include "gl_traits.h"
-#include "special_members.h"
-#include <glad/gl.h>
-
-class VertexArrayObject {
-public:
- template<typename T> [[nodiscard]] VertexArrayObject(const T & arrayObject)
- {
- glBindVertexArray(arrayObject);
- }
-
- ~VertexArrayObject()
- {
- glBindVertexArray(0);
- }
-
- NO_MOVE(VertexArrayObject);
- NO_COPY(VertexArrayObject);
-
- template<typename m, typename T> struct MP {
- constexpr MP(m T::*p) : P {p} { }
-
- operator void *() const
- {
- return &(static_cast<T *>(nullptr)->*P);
- }
-
- m T::*P;
- using value_type = m;
- };
-
- template<typename m, typename T> MP(m T::*) -> MP<m, T>;
-
- template<typename VertexT, MP... attribs>
- VertexArrayObject &
- addAttribs(const GLuint arrayBuffer, const SequentialCollection<VertexT> auto & vertices, const GLuint divisor = 0)
- {
- addAttribs<VertexT, attribs...>(arrayBuffer, divisor);
- data(vertices, arrayBuffer, GL_ARRAY_BUFFER);
- return *this;
- }
-
- template<typename VertexT, MP... attribs>
- VertexArrayObject &
- addAttribs(const GLuint arrayBuffer, const GLuint divisor = 0)
- {
- configure_attribs<VertexT, attribs...>(arrayBuffer, divisor);
- return *this;
- }
-
- // Customisation point
- template<typename VertexT> VertexArrayObject & addAttribsFor(const GLuint arrayBuffer, const GLuint divisor = 0);
-
- template<typename Indices>
- VertexArrayObject &
- addIndices(const GLuint arrayBuffer, const Indices & indices)
- {
- data(indices, arrayBuffer, GL_ELEMENT_ARRAY_BUFFER);
- return *this;
- }
-
- VertexArrayObject &
- addIndices(const GLuint arrayBuffer)
- {
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, arrayBuffer);
- return *this;
- }
-
- template<typename Data>
- static void
- data(const Data & data, const GLuint arrayBuffer, GLenum target)
- {
- using Value = typename Data::value_type;
- glBindBuffer(target, arrayBuffer);
- glBufferData(target, static_cast<GLsizeiptr>(sizeof(Value) * data.size()), data.data(), GL_STATIC_DRAW);
- }
-
-private:
- template<typename VertexT, typename T>
- static auto
- set_pointer(const GLuint vertexArrayId, const void * ptr, const GLuint divisor)
- {
- using traits = gl_traits<T>;
- const auto usedAttribs
- = traits::vertexAttribFunc(vertexArrayId, traits::size, traits::type, sizeof(VertexT), ptr);
- for (GLuint i {}; i < usedAttribs; i++) {
- glEnableVertexAttribArray(vertexArrayId + i);
- glVertexAttribDivisor(vertexArrayId + i, divisor);
- }
- return usedAttribs;
- }
-
- template<typename VertexT, MP attrib>
- static auto
- set_pointer(const GLuint vertexArrayId, const GLuint divisor)
- {
- return set_pointer<VertexT, typename decltype(attrib)::value_type>(vertexArrayId, attrib, divisor);
- }
-
- template<typename VertexT, MP... attribs>
- void
- configure_attribs(const GLuint arrayBuffer, const GLuint divisor)
- {
- glBindBuffer(GL_ARRAY_BUFFER, arrayBuffer);
- if constexpr (sizeof...(attribs) == 0) {
- vertexArrayId += set_pointer<VertexT, VertexT>(vertexArrayId, nullptr, divisor);
- }
- else {
- ((vertexArrayId += set_pointer<VertexT, attribs>(vertexArrayId, divisor)), ...);
- }
- }
-
- GLuint vertexArrayId {};
-};
diff --git a/gfx/lightDirection.cpp b/gfx/lightDirection.cpp
new file mode 100644
index 0000000..5198bdf
--- /dev/null
+++ b/gfx/lightDirection.cpp
@@ -0,0 +1,25 @@
+#include "lightDirection.h"
+#include "maths.h"
+
+constexpr auto TWILIGHT_START = .0_degrees;
+constexpr auto TWILIGHT_END = -18.0_degrees;
+constexpr auto TWILIGHT_RANGE = TWILIGHT_START - TWILIGHT_END;
+
+constexpr auto SUN_ANGLUAR_SIZE = 0.5_degrees;
+constexpr auto SUN_ANGLUAR_RADIUS = SUN_ANGLUAR_SIZE / 2;
+constexpr auto SUN_ELEVATION_REFRACTION_OFFSET = 0.5_degrees;
+constexpr auto SUN_ANGLUAR_OFFSET = SUN_ANGLUAR_RADIUS + SUN_ELEVATION_REFRACTION_OFFSET;
+
+constexpr auto ATMOSPHERE_SCATTER_MIN = .4F;
+constexpr auto ATMOSPHERE_SCATTER_MAX = .7F;
+constexpr auto ATMOSPHERE_SCATTER_RANGE = ATMOSPHERE_SCATTER_MAX - ATMOSPHERE_SCATTER_MIN;
+
+constexpr auto NORM = 0.5F;
+
+LightDirection::LightDirection(const Direction2D sunPos) :
+ pos {sunPos}, vec {glm::mat3 {rotate_yp(pi + sunPos.x, -sunPos.y)} * north},
+ amb {glm::clamp((sunPos.y - TWILIGHT_END) / TWILIGHT_RANGE, 0.F, 1.F)},
+ dir {(-std::cos(std::clamp((sunPos.y + SUN_ANGLUAR_OFFSET) / SUN_ANGLUAR_SIZE, 0.F, 1.F) * pi) * NORM) + NORM},
+ atmosScatter {((half_pi - sunPos.y) / half_pi * ATMOSPHERE_SCATTER_RANGE) + ATMOSPHERE_SCATTER_MIN}
+{
+}
diff --git a/gfx/lightDirection.h b/gfx/lightDirection.h
new file mode 100644
index 0000000..296f497
--- /dev/null
+++ b/gfx/lightDirection.h
@@ -0,0 +1,46 @@
+#pragma once
+
+#include "config/types.h"
+
+class LightDirection {
+public:
+ // NOLINTNEXTLINE(hicpp-explicit-conversions) deliberately a helper
+ LightDirection(Direction2D sunPos);
+
+ [[nodiscard]] Direction2D
+ position() const noexcept
+ {
+ return pos;
+ }
+
+ [[nodiscard]] Direction3D
+ vector() const noexcept
+ {
+ return vec;
+ }
+
+ [[nodiscard]] float
+ ambient() const noexcept
+ {
+ return amb;
+ }
+
+ [[nodiscard]] float
+ directional() const noexcept
+ {
+ return dir;
+ }
+
+ [[nodiscard]] float
+ atmosphericScattering() const noexcept
+ {
+ return atmosScatter;
+ }
+
+private:
+ Direction2D pos;
+ Direction3D vec;
+ float amb;
+ float dir;
+ float atmosScatter;
+};
diff --git a/gfx/models/lights.cpp b/gfx/models/lights.cpp
new file mode 100644
index 0000000..8c0e9e6
--- /dev/null
+++ b/gfx/models/lights.cpp
@@ -0,0 +1,11 @@
+#include "lights.h"
+
+SpotLightVertex::SpotLightVertex(const SpotLightDef & light, uint32_t parentObjectIdx) :
+ SpotLightDef {light}, LightCommonVertex {parentObjectIdx}
+{
+}
+
+PointLightVertex::PointLightVertex(const PointLightDef & light, uint32_t parentObjectIdx) :
+ PointLightDef {light}, LightCommonVertex {parentObjectIdx}
+{
+}
diff --git a/gfx/models/lights.h b/gfx/models/lights.h
new file mode 100644
index 0000000..586b3ef
--- /dev/null
+++ b/gfx/models/lights.h
@@ -0,0 +1,29 @@
+#pragma once
+
+#include "config/types.h"
+
+struct LightCommon {
+ RelativePosition3D position;
+ RGB colour;
+ RelativeDistance kq;
+};
+
+struct LightCommonVertex {
+ uint32_t parentObject;
+};
+
+struct SpotLightDef : LightCommon {
+ Direction3D direction;
+ Angle arc;
+};
+
+struct PointLightDef : LightCommon { };
+
+struct SpotLightVertex : SpotLightDef, LightCommonVertex {
+ SpotLightVertex(const SpotLightDef &, uint32_t);
+};
+
+struct PointLightVertex : PointLightDef, LightCommonVertex {
+ PointLightVertex(const PointLightDef &, uint32_t);
+};
+
diff --git a/gfx/models/mesh.cpp b/gfx/models/mesh.cpp
index e7474ca..6a53f52 100644
--- a/gfx/models/mesh.cpp
+++ b/gfx/models/mesh.cpp
@@ -1,23 +1,54 @@
#include "mesh.h"
-MeshBase::MeshBase(GLsizei m_numIndices, GLenum mode) : m_numIndices {m_numIndices}, mode {mode} { }
+MeshBase::MeshBase(
+ GLsizei m_numIndices, GLenum mode, const std::vector<RelativePosition3D> & positions, GLsizei vertexStride) :
+ vertexStride {vertexStride}, numIndices {m_numIndices}, mode {mode}, dimensions {positions}
+{
+}
+
+MeshBase::Dimensions::Dimensions(const std::span<const RelativePosition3D> positions) :
+ Dimensions {positions, {extents(positions, 0), extents(positions, 1), extents(positions, 2)}}
+{
+}
+
+MeshBase::Dimensions::Dimensions(
+ const std::span<const RelativePosition3D> positions, const std::array<Extents1D, 3> & extents1ds) :
+ minExtent(extents1ds[0].min, extents1ds[1].min, extents1ds[2].min),
+ maxExtent(extents1ds[0].max, extents1ds[1].max, extents1ds[2].max), centre {(minExtent + maxExtent) / 2.0F},
+ size {std::ranges::max(positions | std::views::transform([this](const auto & v) {
+ return glm::distance(v, centre);
+ }))}
+{
+}
+
+MeshBase::Dimensions::Extents1D
+MeshBase::Dimensions::extents(const std::span<const RelativePosition3D> positions, glm::length_t D)
+{
+ return std::ranges::minmax(positions | std::views::transform([D](const auto & v) {
+ return v[D];
+ }));
+}
void
-MeshBase::Draw() const
+MeshBase::draw() const
{
- glBindVertexArray(m_vertexArrayObject);
+ glBindVertexArray(*vertexArrayObject);
+ glVertexArrayVertexBuffer(*vertexArrayObject, 0, vertexArrayBuffers[0], 0, vertexStride);
+ glVertexArrayElementBuffer(*vertexArrayObject, vertexArrayBuffers[1]);
- glDrawElements(mode, m_numIndices, GL_UNSIGNED_INT, nullptr);
+ glDrawElements(mode, numIndices, GL_UNSIGNED_INT, nullptr);
glBindVertexArray(0);
}
void
-MeshBase::DrawInstanced(GLuint vao, GLsizei count, GLuint base) const
+MeshBase::drawInstanced(GLuint vao, GLsizei count, GLuint base) const
{
glBindVertexArray(vao);
+ glVertexArrayVertexBuffer(vao, 0, vertexArrayBuffers[0], 0, vertexStride);
+ glVertexArrayElementBuffer(vao, vertexArrayBuffers[1]);
- glDrawElementsInstancedBaseInstance(mode, m_numIndices, GL_UNSIGNED_INT, nullptr, count, base);
+ glDrawElementsInstancedBaseInstance(mode, numIndices, GL_UNSIGNED_INT, nullptr, count, base);
glBindVertexArray(0);
}
diff --git a/gfx/models/mesh.h b/gfx/models/mesh.h
index 248cb8f..fa12ae8 100644
--- a/gfx/models/mesh.h
+++ b/gfx/models/mesh.h
@@ -1,8 +1,11 @@
#pragma once
-#include "gfx/gl/vertexArrayObject.h"
-#include <glArrays.h>
+#include "config/types.h"
+#include "gfx/gl/glBuffer.h"
+#include "gfx/gl/gldebug.h"
+#include <gfx/gl/glVertexArray.h>
#include <glad/gl.h>
+#include <ranges>
#include <span>
#include <stdTypeDefs.h>
@@ -10,33 +13,69 @@ class Vertex;
class MeshBase {
public:
- void Draw() const;
- void DrawInstanced(GLuint vao, GLsizei count, GLuint base = 0) const;
+ class Dimensions {
+ public:
+ using Extents1D = std::ranges::minmax_result<RelativeDistance>;
+ explicit Dimensions(std::span<const RelativePosition3D>);
+
+ RelativePosition3D minExtent, maxExtent;
+ RelativePosition3D centre;
+ RelativeDistance size;
+
+ private:
+ Dimensions(std::span<const RelativePosition3D>, const std::array<Extents1D, 3> &);
+ static Extents1D extents(std::span<const RelativePosition3D>, glm::length_t);
+ };
+
+ void draw() const;
+ void drawInstanced(GLuint vao, GLsizei count, GLuint base = 0) const;
+
+ [[nodiscard]] const Dimensions &
+ getDimensions() const
+ {
+ return dimensions;
+ }
protected:
- MeshBase(GLsizei m_numIndices, GLenum mode);
+ MeshBase(GLsizei numIndices, GLenum mode, const std::vector<RelativePosition3D> &, GLsizei vertexStride);
- glVertexArray m_vertexArrayObject;
- glBuffers<2> m_vertexArrayBuffers;
- GLsizei m_numIndices;
+ std::shared_ptr<glVertexArray> vertexArrayObject;
+ glBuffers<2> vertexArrayBuffers;
+ GLsizei vertexStride;
+ GLsizei numIndices;
GLenum mode;
+ Dimensions dimensions;
};
template<typename V> class MeshT : public MeshBase, public ConstTypeDefs<MeshT<V>> {
public:
MeshT(const std::span<const V> vertices, const std::span<const unsigned int> indices, GLenum mode = GL_TRIANGLES) :
- MeshBase {static_cast<GLsizei>(indices.size()), mode}
+ MeshBase {static_cast<GLsizei>(indices.size()), mode,
+ materializeRange(vertices | std::views::transform([](const auto & vertex) {
+ return static_cast<RelativePosition3D>(vertex.pos);
+ })),
+ sizeof(V)}
{
- VertexArrayObject::data(vertices, m_vertexArrayBuffers[0], GL_ARRAY_BUFFER);
- VertexArrayObject::data(indices, m_vertexArrayBuffers[1], GL_ARRAY_BUFFER);
- configureVAO(m_vertexArrayObject);
+ glDebugScope _ {0};
+ vertexArrayBuffers[0].storage(vertices, 0);
+ vertexArrayBuffers[1].storage(indices, 0);
+ if (!(vertexArrayObject = commonVertexArrayObject.lock())) {
+ commonVertexArrayObject = vertexArrayObject = std::make_shared<glVertexArray>();
+ configureVAO(*vertexArrayObject, 0);
+ }
}
- VertexArrayObject &
- configureVAO(VertexArrayObject && vao) const
+ auto
+ configureVAO(glVertexArray & vao, GLuint divisor) const
{
- return vao.addAttribsFor<V>(m_vertexArrayBuffers[0]).addIndices(m_vertexArrayBuffers[1]);
+ glDebugScope _ {0};
+ return vao.configure().addAttribsFor<V>(divisor);
}
+
+protected:
+ static std::weak_ptr<glVertexArray> commonVertexArrayObject;
};
+template<typename T> std::weak_ptr<glVertexArray> MeshT<T>::commonVertexArrayObject;
+
using Mesh = MeshT<Vertex>;
diff --git a/gfx/models/texture.cpp b/gfx/models/texture.cpp
index 51223aa..5a104be 100644
--- a/gfx/models/texture.cpp
+++ b/gfx/models/texture.cpp
@@ -1,7 +1,5 @@
#include "texture.h"
#include "config/types.h"
-#include "glArrays.h"
-#include "tga.h"
#include <fcntl.h>
#include <filesystem.h>
#include <gfx/image.h>
@@ -12,6 +10,8 @@
#include <stb/stb_image.h>
#include <sys/mman.h>
+using std::ceil;
+
GLint
TextureOptions::glMapMode(TextureOptions::MapMode mm)
{
@@ -33,117 +33,66 @@ Texture::Texture(const std::filesystem::path & fileName, TextureOptions to) :
}
Texture::Texture(const Image & tex, TextureOptions to) :
- Texture {static_cast<GLsizei>(tex.width), static_cast<GLsizei>(tex.height), tex.data.data(), to}
-{
-}
-
-Texture::Texture(GLsizei width, GLsizei height, TextureOptions to) : Texture {width, height, nullptr, to} { }
-
-Texture::Texture(GLsizei width, GLsizei height, const void * data, TextureOptions to) : type {to.type}
-{
- glBindTexture(type, m_texture);
- glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
-
- glTexParameter(type, GL_TEXTURE_WRAP_S, TextureOptions::glMapMode(to.wrapU));
- glTexParameter(type, GL_TEXTURE_WRAP_T, TextureOptions::glMapMode(to.wrapV));
-
- glTexParameter(type, GL_TEXTURE_MIN_FILTER, to.minFilter);
- glTexParameter(type, GL_TEXTURE_MAG_FILTER, to.magFilter);
- glTexImage2D(type, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
-}
-
-void
-Texture::bind(GLenum unit) const
+ Texture {static_cast<GLsizei>(tex.width), static_cast<GLsizei>(tex.height), GL_RGBA, GL_UNSIGNED_BYTE,
+ tex.data.data(), to}
{
- glActiveTexture(unit);
- glBindTexture(type, m_texture);
}
-TextureAbsCoord
-Texture::getSize(const glTexture & texture)
+Texture::Texture(GLsizei width, GLsizei height, TextureOptions to)
{
- TextureAbsCoord size;
- glGetTextureLevelParameteriv(texture, 0, GL_TEXTURE_WIDTH, &size.x);
- glGetTextureLevelParameteriv(texture, 0, GL_TEXTURE_HEIGHT, &size.y);
- return size;
+ const auto levels = static_cast<GLsizei>(ceil(std::log2(std::max(width, height))));
+ m_texture.storage(levels, GL_RGBA8, {width, height});
+ m_texture.parameter(GL_TEXTURE_WRAP_S, TextureOptions::glMapMode(to.wrapU));
+ m_texture.parameter(GL_TEXTURE_WRAP_T, TextureOptions::glMapMode(to.wrapV));
+ m_texture.parameter(GL_TEXTURE_MIN_FILTER, to.minFilter);
+ m_texture.parameter(GL_TEXTURE_MAG_FILTER, to.magFilter);
}
-void
-Texture::save(
- const glTexture & texture, GLenum format, GLenum type, uint8_t channels, const char * path, uint8_t tgaFormat)
+Texture::Texture(GLsizei width, GLsizei height, GLenum pixelFormat, GLenum PixelType, const void * pixels,
+ TextureOptions to) : Texture {width, height, to}
{
- const auto size = getSize(texture);
- const size_t dataSize = (static_cast<size_t>(size.x * size.y * channels));
- const size_t fileSize = dataSize + sizeof(TGAHead);
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
- filesystem::fh out {path, O_RDWR | O_CREAT, 0660};
- out.truncate(fileSize);
- auto tga = out.mmap(fileSize, 0, PROT_WRITE, MAP_SHARED);
- *tga.get<TGAHead>() = {
- .format = tgaFormat,
- .size = size,
- .pixelDepth = static_cast<uint8_t>(8 * channels),
+ m_texture.image({width, height}, pixelFormat, PixelType, pixels);
+ auto isMimmap = [](auto value) {
+ auto eqAnyOf = [value](auto... test) {
+ return (... || (value == test));
+ };
+ return eqAnyOf(
+ GL_NEAREST_MIPMAP_NEAREST, GL_LINEAR_MIPMAP_NEAREST, GL_NEAREST_MIPMAP_LINEAR, GL_LINEAR_MIPMAP_LINEAR);
};
- glPixelStorei(GL_PACK_ALIGNMENT, 1);
- glGetTextureImage(texture, 0, format, type, static_cast<GLsizei>(dataSize), tga.get<TGAHead>() + 1);
- tga.msync(MS_ASYNC);
-}
-
-void
-Texture::save(const char * path) const
-{
- save(m_texture, GL_BGR, GL_UNSIGNED_BYTE, 3, path, 2);
-}
-
-void
-Texture::save(const glTexture & texture, const char * path)
-{
- save(texture, GL_BGR, GL_UNSIGNED_BYTE, 3, path, 2);
-}
-
-void
-Texture::savePosition(const glTexture & texture, const char * path)
-{
- save(texture, GL_BGR_INTEGER, GL_UNSIGNED_BYTE, 3, path, 2);
-}
-
-void
-Texture::saveDepth(const glTexture & texture, const char * path)
-{
- save(texture, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, 1, path, 3);
+ const auto levels = static_cast<GLsizei>(ceil(std::log2(std::max(width, height))));
+ if (levels > 1 && (isMimmap(to.minFilter) || isMimmap(to.magFilter))) {
+ m_texture.generateMipmap();
+ }
}
void
-Texture::saveNormal(const glTexture & texture, const char * path)
+Texture::bind(GLenum unit) const
{
- save(texture, GL_BGR, GL_BYTE, 3, path, 2);
+ m_texture.bind(unit);
}
-TextureAtlas::TextureAtlas(GLsizei width, GLsizei height, GLuint count) : Texture(width, height, nullptr, {})
+TextureAtlas::TextureAtlas(GLsizei width, GLsizei height, GLuint count) : Texture(width, height)
{
- glBindTexture(GL_TEXTURE_RECTANGLE, m_atlas);
-
- glTexParameter(GL_TEXTURE_RECTANGLE, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- glTexParameter(GL_TEXTURE_RECTANGLE, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
-
- glTexParameter(GL_TEXTURE_RECTANGLE, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
- glTexParameter(GL_TEXTURE_RECTANGLE, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
- glTexImage2D(GL_TEXTURE_RECTANGLE, 0, GL_RGBA16UI, 2, static_cast<GLsizei>(count), 0, GL_RGBA_INTEGER,
- GL_UNSIGNED_BYTE, nullptr);
+ m_atlas.parameter(GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ m_atlas.parameter(GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ m_atlas.parameter(GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ m_atlas.parameter(GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ m_atlas.storage(1, GL_RGBA16UI, {2, count});
}
void
TextureAtlas::bind(GLenum unit) const
{
Texture::bind(unit);
- glActiveTexture(unit + 1);
- glBindTexture(GL_TEXTURE_RECTANGLE, m_atlas);
+ m_atlas.bind(unit + 1);
}
GLuint
TextureAtlas::add(TextureAbsCoord position, TextureAbsCoord size, void * data, TextureOptions to)
{
- glTextureSubImage2D(m_texture, 0, position.x, position.y, size.x, size.y, GL_RGBA, GL_UNSIGNED_BYTE, data);
+ m_texture.subImage(position, size, GL_RGBA, GL_UNSIGNED_BYTE, data);
struct Material {
glm::vec<2, uint16_t> position, size;
@@ -152,6 +101,12 @@ TextureAtlas::add(TextureAbsCoord position, TextureAbsCoord size, void * data, T
} material {position, size, to.wrapU, to.wrapV};
static_assert(sizeof(Material) <= 32);
- glTextureSubImage2D(m_atlas, 0, 0, static_cast<GLsizei>(used), 2, 1, GL_RGBA_INTEGER, GL_UNSIGNED_SHORT, &material);
+ m_atlas.subImage({0, used}, {2, 1}, GL_RGBA_INTEGER, GL_UNSIGNED_SHORT, &material);
return ++used;
}
+
+void
+TextureAtlas::complete()
+{
+ m_texture.generateMipmap();
+}
diff --git a/gfx/models/texture.h b/gfx/models/texture.h
index 8cb8128..9c67ae1 100644
--- a/gfx/models/texture.h
+++ b/gfx/models/texture.h
@@ -1,7 +1,7 @@
#pragma once
#include "config/types.h"
-#include "glArrays.h"
+#include "gfx/gl/glTexture.h"
#include "stdTypeDefs.h"
#include <filesystem>
#include <glm/fwd.hpp>
@@ -17,7 +17,6 @@ struct TextureOptions {
};
MapMode wrapU {MapMode::Repeat}, wrapV {MapMode::Repeat};
GLint minFilter {GL_LINEAR}, magFilter {GL_LINEAR};
- GLenum type {GL_TEXTURE_2D};
static GLint glMapMode(MapMode);
};
@@ -29,32 +28,24 @@ public:
explicit Texture(const std::filesystem::path & fileName, TextureOptions = {});
explicit Texture(const Image & image, TextureOptions = {});
explicit Texture(GLsizei width, GLsizei height, TextureOptions = {});
- explicit Texture(GLsizei width, GLsizei height, const void * data, TextureOptions = {});
+ explicit Texture(GLsizei width, GLsizei height, GLenum pixelFormat, GLenum PixelType, const void * pixels,
+ TextureOptions = {});
- virtual void bind(GLenum unit = GL_TEXTURE0) const;
-
- void save(const char * path) const;
- static void save(const glTexture &, const char * path);
- static void saveDepth(const glTexture &, const char * path);
- static void saveNormal(const glTexture &, const char * path);
- static void savePosition(const glTexture &, const char * path);
+ virtual void bind(GLuint unit) const;
protected:
- static void save(const glTexture &, GLenum, GLenum, uint8_t channels, const char * path, uint8_t tgaFormat);
- static TextureAbsCoord getSize(const glTexture &);
-
- glTexture m_texture;
- GLenum type;
+ glTexture<GL_TEXTURE_2D> m_texture;
};
class TextureAtlas : public Texture {
public:
TextureAtlas(GLsizei width, GLsizei height, GLuint count);
- void bind(GLenum unit = GL_TEXTURE0) const override;
+ void bind(GLuint unit) const override;
GLuint add(TextureAbsCoord position, TextureAbsCoord size, void * data, TextureOptions = {});
+ void complete();
private:
- glTexture m_atlas;
+ glTexture<GL_TEXTURE_RECTANGLE> m_atlas;
GLuint used {};
};
diff --git a/gfx/models/tga.h b/gfx/models/tga.h
index 3d072fb..955e5e1 100644
--- a/gfx/models/tga.h
+++ b/gfx/models/tga.h
@@ -3,14 +3,15 @@
#include <cstdint>
#include <glm/vec2.hpp>
-struct TGAHead {
+template<glm::length_t Channels> struct TGAHead {
using XY = glm::vec<2, uint16_t>;
+ using PixelType = glm::vec<Channels, uint8_t>;
+
uint8_t idLength {}, colorMapType {}, format {};
uint16_t __attribute__((packed)) colorMapFirst {}, colorMapLength {};
uint8_t colorMapEntrySize {};
XY origin {}, size {};
- uint8_t pixelDepth {};
+ uint8_t pixelDepth {8 * Channels};
uint8_t descriptor {};
+ PixelType data[1] {};
};
-
-static_assert(sizeof(TGAHead) == 18);
diff --git a/gfx/models/vertex.cpp b/gfx/models/vertex.cpp
index c144db3..4b5ce54 100644
--- a/gfx/models/vertex.cpp
+++ b/gfx/models/vertex.cpp
@@ -1,10 +1,10 @@
#include "vertex.h"
-#include "gfx/gl/vertexArrayObject.h"
+#include "gfx/gl/glVertexArray.h"
template<>
-VertexArrayObject &
-VertexArrayObject::addAttribsFor<Vertex>(const GLuint arrayBuffer, const GLuint divisor)
+Impl::VertexArrayConfigurator &
+Impl::VertexArrayConfigurator::addAttribsFor<Vertex>(const GLuint divisor)
{
return addAttribs<Vertex, &Vertex::pos, &Vertex::texCoord, &Vertex::normal, &Vertex::colour, &Vertex::material>(
- arrayBuffer, divisor);
+ divisor);
}
diff --git a/gfx/renderable.cpp b/gfx/renderable.cpp
index 0340189..8523118 100644
--- a/gfx/renderable.cpp
+++ b/gfx/renderable.cpp
@@ -1,11 +1,100 @@
#include "renderable.h"
+#include "gfx/gl/sceneShader.h"
+#include "gl_traits.h"
+#include "location.h"
+#include "maths.h"
+#include "util.h"
+
+std::weak_ptr<Renderable::CommonLocationData> Renderable::commonLocationData;
+std::weak_ptr<Renderable::CommonSpotLights> Renderable::commonSpotLights;
+std::weak_ptr<Renderable::CommonPointLights> Renderable::commonPointLights;
+std::weak_ptr<glVertexArray> Renderable::commonInstancesSpotLightVAO, Renderable::commonInstancesPointLightVAO;
+
+Renderable::CommonLocation::CommonLocation(Location const & location) :
+ position {location.pos, 0}, rotation {location.rot, 0}, rotationMatrix {location.getRotationTransform()}
+{
+}
+
+Renderable::CommonLocation &
+Renderable::CommonLocation ::operator=(Location const & location)
+{
+ position = location.pos || 0;
+ rotation = location.rot || 0.F;
+ rotationMatrix = location.getRotationTransform();
+ return *this;
+}
+
+Renderable::Renderable()
+{
+ createIfRequired(locationData, commonLocationData);
+ createIfRequired(spotLights, commonSpotLights);
+ createIfRequired(pointLights, commonPointLights);
+ if (createIfRequired(instancesSpotLightVAO, commonInstancesSpotLightVAO)) {
+ instancesSpotLightVAO->configure()
+ .addAttribs<SpotLightVertex, &SpotLightVertex::position, &SpotLightVertex::direction,
+ &SpotLightVertex::colour, &SpotLightVertex::kq, &SpotLightVertex::arc,
+ &SpotLightVertex::parentObject>(0);
+ }
+ if (createIfRequired(instancesPointLightVAO, commonInstancesPointLightVAO)) {
+ instancesPointLightVAO->configure()
+ .addAttribs<PointLightVertex, &PointLightVertex::position, &PointLightVertex::colour,
+ &PointLightVertex::kq, &PointLightVertex::parentObject>(0);
+ }
+}
+
+GLuint
+gl_traits<InstanceVertices<Renderable::CommonLocation>::InstanceProxy>::vertexArrayAttribFormat(
+ GLuint vao, GLuint index, GLuint offset)
+{
+ return gl_traits<
+ decltype(InstanceVertices<Renderable::CommonLocation>::InstanceProxy::index)>::vertexArrayAttribFormat(vao,
+ index, offset + offsetof(InstanceVertices<Renderable::CommonLocation>::InstanceProxy, index));
+};
+
+void
+Renderable::preFrame(const Frustum &, const Frustum &)
+{
+}
+
+void
+Renderable::lights(const SceneShader & shader)
+{
+ glDebugScope _ {0};
+ if (const auto instancesSpotLight = commonSpotLights.lock()) {
+ if (const auto scount = instancesSpotLight->size()) {
+ if (const auto instancesSpotLightVAO = commonInstancesSpotLightVAO.lock()) {
+ glDebugScope _ {*instancesSpotLightVAO, "Spot lights"};
+ shader.spotLightInst.use();
+ glBindVertexArray(*instancesSpotLightVAO);
+ instancesSpotLightVAO->useBuffer(0, *instancesSpotLight);
+ glDrawArrays(GL_POINTS, 0, static_cast<GLsizei>(scount));
+ }
+ }
+ }
+ if (const auto instancesPointLight = commonPointLights.lock()) {
+ if (const auto pcount = instancesPointLight->size()) {
+ if (const auto instancesPointLightVAO = commonInstancesPointLightVAO.lock()) {
+ glDebugScope _ {*instancesPointLightVAO, "Point lights"};
+ shader.pointLightInst.use();
+ glBindVertexArray(*instancesPointLightVAO);
+ instancesPointLightVAO->useBuffer(0, *instancesPointLight);
+ glDrawArrays(GL_POINTS, 0, static_cast<GLsizei>(pcount));
+ }
+ }
+ }
+}
+
+void
+Renderable::shadows(const ShadowMapper &, const Frustum &) const
+{
+}
void
-Renderable::lights(const SceneShader &) const
+Renderable::updateStencil(const ShadowStenciller &) const
{
}
void
-Renderable::shadows(const ShadowMapper &) const
+Renderable::updateBillboard(const BillboardPainter &) const
{
}
diff --git a/gfx/renderable.h b/gfx/renderable.h
index e126fff..7f4f52e 100644
--- a/gfx/renderable.h
+++ b/gfx/renderable.h
@@ -1,17 +1,58 @@
#pragma once
+#include "gfx/gl/glVertexArray.h"
+#include "gfx/gl/instanceVertices.h"
+#include "gfx/models/lights.h"
+#include "gl_traits.h"
+#include <glm/mat3x3.hpp>
#include <special_members.h>
class SceneShader;
+class Frustum;
class ShadowMapper;
+class ShadowStenciller;
+class BillboardPainter;
+class Location;
class Renderable {
public:
- Renderable() = default;
+ Renderable();
virtual ~Renderable() = default;
DEFAULT_MOVE_COPY(Renderable);
- virtual void render(const SceneShader & shader) const = 0;
- virtual void lights(const SceneShader & shader) const;
- virtual void shadows(const ShadowMapper & shadowMapper) const;
+ virtual void preFrame(const Frustum &, const Frustum &);
+ virtual void render(const SceneShader & shader, const Frustum &) const = 0;
+ static void lights(const SceneShader & shader);
+ virtual void shadows(const ShadowMapper & shadowMapper, const Frustum &) const;
+
+ virtual void updateStencil(const ShadowStenciller & lightDir) const;
+ virtual void updateBillboard(const BillboardPainter &) const;
+
+ struct CommonLocation {
+ CommonLocation(const Location &);
+ CommonLocation & operator=(const Location &);
+
+ glm::ivec4 position;
+ glm::vec4 rotation;
+ glm::mat3x4 rotationMatrix;
+ };
+
+ using CommonLocationData = InstanceVertices<CommonLocation>;
+ using CommonLocationInstance = CommonLocationData::InstanceProxy;
+ std::shared_ptr<CommonLocationData> locationData;
+ static std::weak_ptr<CommonLocationData> commonLocationData;
+
+ using CommonSpotLights = InstanceVertices<SpotLightVertex>;
+ std::shared_ptr<CommonSpotLights> spotLights;
+ static std::weak_ptr<CommonSpotLights> commonSpotLights;
+ using CommonPointLights = InstanceVertices<PointLightVertex>;
+ std::shared_ptr<CommonPointLights> pointLights;
+ static std::weak_ptr<CommonPointLights> commonPointLights;
+ std::shared_ptr<glVertexArray> instancesSpotLightVAO, instancesPointLightVAO;
+ static std::weak_ptr<glVertexArray> commonInstancesSpotLightVAO, commonInstancesPointLightVAO;
+};
+
+template<> struct gl_traits<InstanceVertices<Renderable::CommonLocation>::InstanceProxy> {
+ static GLuint vertexArrayAttribFormat(GLuint vao, GLuint index, GLuint offset);
};
+