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