diff options
author | Dan Goodliffe <dan@randomdan.homeip.net> | 2023-04-22 19:06:42 +0100 |
---|---|---|
committer | Dan Goodliffe <dan@randomdan.homeip.net> | 2023-04-22 19:06:42 +0100 |
commit | d1f79b4c438ebf0822741e103b2cb06bdee4514e (patch) | |
tree | a4d574d12cd81d5e33886ce7ca961f7dbe872fcf | |
parent | Rename lots of shader files (diff) | |
parent | Keep the instance unused vector sorted and binary search it (diff) | |
download | ilt-d1f79b4c438ebf0822741e103b2cb06bdee4514e.tar.bz2 ilt-d1f79b4c438ebf0822741e103b2cb06bdee4514e.tar.xz ilt-d1f79b4c438ebf0822741e103b2cb06bdee4514e.zip |
Merge branch 'instancing'
-rw-r--r-- | application/main.cpp | 8 | ||||
-rw-r--r-- | game/gamestate.h | 2 | ||||
-rw-r--r-- | game/scenary/foliage.cpp | 22 | ||||
-rw-r--r-- | game/scenary/foliage.h | 10 | ||||
-rw-r--r-- | game/scenary/plant.cpp | 12 | ||||
-rw-r--r-- | game/scenary/plant.h | 12 | ||||
-rw-r--r-- | gfx/gl/instanceVertices.h | 211 | ||||
-rw-r--r-- | gfx/gl/sceneRenderer.cpp | 6 | ||||
-rw-r--r-- | gfx/gl/sceneShader.cpp | 29 | ||||
-rw-r--r-- | gfx/gl/sceneShader.h | 3 | ||||
-rw-r--r-- | gfx/gl/shaders/dynamicPointInst.vs | 21 | ||||
-rw-r--r-- | gfx/gl/shaders/spotLight.vs | 2 | ||||
-rw-r--r-- | gfx/gl/vertexArrayObject.hpp | 107 | ||||
-rw-r--r-- | gfx/models/mesh.cpp | 27 | ||||
-rw-r--r-- | gfx/models/mesh.h | 4 | ||||
-rw-r--r-- | lib/gl_traits.hpp | 17 | ||||
-rw-r--r-- | test/Jamfile.jam | 3 | ||||
-rw-r--r-- | test/test-assetFactory.cpp | 8 | ||||
-rw-r--r-- | test/test-instancing.cpp | 181 | ||||
-rw-r--r-- | test/test-render.cpp | 3 | ||||
-rw-r--r-- | test/testMainWindow.cpp | 3 | ||||
-rw-r--r-- | ui/gameMainWindow.cpp | 10 |
22 files changed, 598 insertions, 103 deletions
diff --git a/application/main.cpp b/application/main.cpp index aea3d2e..9feb80b 100644 --- a/application/main.cpp +++ b/application/main.cpp @@ -44,9 +44,9 @@ public: windows.create<GameMainWindow>(DISPLAY_WIDTH, DISPLAY_HEIGHT); world.create<Terrain>(geoData); + assets = AssetFactory::loadAll("res"); { - const auto assets = AssetFactory::loadAll("res"); auto rl = world.create<RailLinks>(); const glm::vec3 j {-1120, -1100, 3}, k {-1100, -1000, 15}, l {-1000, -800, 20}, m {-900, -600, 30}, n {-600, -500, 32}, o {-500, -800, 30}, p {-600, -900, 25}, q {-1025, -1175, 10}, @@ -79,7 +79,11 @@ public: train->currentActivity = train->orders.current()->createActivity(); auto foliage = std::dynamic_pointer_cast<Foliage>(assets.at("Tree-01-1")); - world.create<Plant>(foliage, Location {{-1100, -1100, 0}}); + for (float x = 900; x < 1100; x += 3) { + for (float y = 900; y < 1100; y += 3) { + world.create<Plant>(foliage, Location {geoData->positionAt({-x, -y})}); + } + } } auto t_start = std::chrono::high_resolution_clock::now(); diff --git a/game/gamestate.h b/game/gamestate.h index 605aac4..db223c0 100644 --- a/game/gamestate.h +++ b/game/gamestate.h @@ -1,5 +1,6 @@ #pragma once +#include "assetFactory/assetFactory.h" #include <collection.hpp> #include <memory> #include <special_members.hpp> @@ -16,5 +17,6 @@ public: Collection<WorldObject> world; std::shared_ptr<GeoData> geoData; + AssetFactory::Assets assets; }; extern GameState * gameState; diff --git a/game/scenary/foliage.cpp b/game/scenary/foliage.cpp index d39d500..35be051 100644 --- a/game/scenary/foliage.cpp +++ b/game/scenary/foliage.cpp @@ -1,7 +1,9 @@ #include "foliage.h" #include "gfx/gl/sceneShader.h" #include "gfx/gl/shadowMapper.h" +#include "gfx/gl/vertexArrayObject.hpp" #include "gfx/models/texture.h" +#include "location.hpp" bool Foliage::persist(Persistence::PersistenceStore & store) @@ -13,21 +15,25 @@ void Foliage::postLoad() { texture = getTexture(); + bodyMesh->configureVAO(instanceVAO).addAttribs<glm::mat4>(instances.bufferName(), 1); } void -Foliage::render(const SceneShader & shader, const Location & loc) const +Foliage::render(const SceneShader & shader) const { - shader.basic.use(loc); - if (texture) { - texture->bind(); + if (const auto count = instances.count()) { + shader.basicInst.use(); + if (texture) { + texture->bind(); + } + glBindVertexArray(instanceVAO); + glDrawElementsInstanced( + bodyMesh->type(), bodyMesh->count(), GL_UNSIGNED_INT, nullptr, static_cast<GLsizei>(count)); + glBindVertexArray(0); } - bodyMesh->Draw(); } void -Foliage::shadows(const ShadowMapper & mapper, const Location & loc) const +Foliage::shadows(const ShadowMapper &) const { - mapper.dynamicPoint.use(loc); - bodyMesh->Draw(); } diff --git a/game/scenary/foliage.h b/game/scenary/foliage.h index b85aab2..b72a9c2 100644 --- a/game/scenary/foliage.h +++ b/game/scenary/foliage.h @@ -1,19 +1,23 @@ #pragma once #include "assetFactory/asset.h" +#include "gfx/gl/instanceVertices.h" +#include "gfx/renderable.h" class SceneShader; class ShadowMapper; class Location; class Texture; -class Foliage : public Asset, public StdTypeDefs<Foliage> { +class Foliage : public Asset, public Renderable, public StdTypeDefs<Foliage> { Mesh::Ptr bodyMesh; std::shared_ptr<Texture> texture; + glVertexArray instanceVAO; public: - void render(const SceneShader &, const Location &) const; - void shadows(const ShadowMapper &, const Location &) const; + mutable InstanceVertices<glm::mat4> instances; + void render(const SceneShader &) const override; + void shadows(const ShadowMapper &) const override; protected: friend Persistence::SelectionPtrBase<std::shared_ptr<Foliage>>; diff --git a/game/scenary/plant.cpp b/game/scenary/plant.cpp index 2b01bee..678d4a7 100644 --- a/game/scenary/plant.cpp +++ b/game/scenary/plant.cpp @@ -1,13 +1,7 @@ #include "plant.h" -void -Plant::render(const SceneShader & shader) const +Plant::Plant(std::shared_ptr<const Foliage> type, Location position) : + type {std::move(type)}, + location {this->type->instances.acquire(glm::translate(position.pos) * rotate_ypr(position.rot))} { - type->render(shader, position); -} - -void -Plant::shadows(const ShadowMapper & mapper) const -{ - type->shadows(mapper, position); } diff --git a/game/scenary/plant.h b/game/scenary/plant.h index 55acca1..77c5979 100644 --- a/game/scenary/plant.h +++ b/game/scenary/plant.h @@ -2,15 +2,13 @@ #include "foliage.h" #include "game/worldobject.h" -#include "gfx/renderable.h" #include "location.hpp" +#include "maths.h" +#include <glm/gtx/transform.hpp> -class Plant : public Renderable, public WorldObject { +class Plant : public WorldObject { std::shared_ptr<const Foliage> type; - Location position; - - void render(const SceneShader & shader) const override; - void shadows(const ShadowMapper & shadowMapper) const override; + InstanceVertices<glm::mat4>::InstanceProxy location; void tick(TickDuration) override @@ -18,5 +16,5 @@ class Plant : public Renderable, public WorldObject { } public: - Plant(std::shared_ptr<const Foliage> type, Location position) : type(std::move(type)), position(position) { } + Plant(std::shared_ptr<const Foliage> type, Location position); }; diff --git a/gfx/gl/instanceVertices.h b/gfx/gl/instanceVertices.h new file mode 100644 index 0000000..228020d --- /dev/null +++ b/gfx/gl/instanceVertices.h @@ -0,0 +1,211 @@ +#pragma once + +#include "glArrays.h" +#include <iterator> +#include <span> +#include <special_members.hpp> +#include <utility> +#include <vector> + +template<typename T> class InstanceVertices { +public: + InstanceVertices(size_t initialSize = 16) + { + allocBuffer(initialSize); + } + + class [[nodiscard]] InstanceProxy { + public: + InstanceProxy(InstanceVertices * iv, std::size_t idx) : instances {iv}, index {idx} { } + InstanceProxy(InstanceProxy && other) : instances {std::exchange(other.instances, nullptr)}, index {other.index} + { + } + NO_COPY(InstanceProxy); + + ~InstanceProxy() + { + if (instances) { + instances->release(index); + } + } + + InstanceProxy & + operator=(InstanceProxy && other) + { + if (instances) { + instances->release(index); + } + instances = std::exchange(other.instances, nullptr); + index = other.index; + return *this; + } + template<typename U> + T & + operator=(U && v) + { + return instances->at(index) = std::forward<U>(v); + } + + [[nodiscard]] + operator T &() + { + return instances->at(index); + } + [[nodiscard]] operator const T &() const + { + return instances->at(index); + } + [[nodiscard]] T * + get() + { + return &instances->at(index); + } + [[nodiscard]] const T * + get() const + { + return &instances->at(index); + } + [[nodiscard]] T * + operator->() + { + return get(); + } + [[nodiscard]] const T * + operator->() const + { + return get(); + } + [[nodiscard]] T & + operator*() + { + return instances->at(index); + } + [[nodiscard]] const T & + operator*() const + { + return instances->at(index); + } + + private: + InstanceVertices<T> * instances; + std::size_t index; + }; + + template<typename... Params> + [[nodiscard]] InstanceProxy + acquire(Params &&... params) + { + map(); + if (!unused.empty()) { + auto idx = unused.back(); + unused.pop_back(); + index[idx] = next++; + new (&at(idx)) T(std::forward<Params>(params)...); + return InstanceProxy {this, idx}; + } + if (next >= capacity) { + resize(capacity * 2); + } + index.emplace_back(next++); + new (data + index.back()) T(std::forward<Params>(params)...); + return InstanceProxy {this, index.size() - 1}; + } + + [[nodiscard]] const auto & + bufferName() const + { + return buffer; + } + + [[nodiscard]] auto + count() const + { + unmap(); + return next; + } + +protected: + friend InstanceProxy; + + void + release(const size_t pidx) + { + // Destroy p's object + at(pidx).~T(); + if (--next != index[pidx]) { + // Move last object into p's slot + new (&at(pidx)) T {std::move(data[next])}; + (data[next]).~T(); + *std::find_if(index.begin(), index.end(), [this](const auto & i) { + return i == next && !std::binary_search(unused.begin(), unused.end(), &i - index.data()); + }) = index[pidx]; + } + if (pidx == index.size() - 1) { + index.pop_back(); + } + else { + // Remember p.index is free index now, keeping it sorted + unused.insert(std::upper_bound(unused.begin(), unused.end(), pidx), pidx); + } + } + + void + allocBuffer(std::size_t newCapacity) + { + glBindBuffer(GL_ARRAY_BUFFER, buffer); + glBufferData(GL_ARRAY_BUFFER, static_cast<GLsizeiptr>(sizeof(T) * newCapacity), nullptr, GL_DYNAMIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); + capacity = newCapacity; + data = nullptr; + } + + void + resize(size_t newCapacity) + { + const auto maintain = std::min(newCapacity, capacity); + std::vector<T> existing; + const auto maintaind = static_cast<typename decltype(existing)::difference_type>(maintain); + existing.reserve(maintain); + map(); + std::move(data, data + maintain, std::back_inserter(existing)); + allocBuffer(newCapacity); + map(); + std::move(existing.begin(), existing.begin() + maintaind, data); + capacity = newCapacity; + } + + [[nodiscard]] T & + at(size_t pindex) + { + map(); + return data[index[pindex]]; + } + + void + map() const + { + if (!data) { + data = static_cast<T *>(glMapNamedBuffer(buffer, GL_READ_WRITE)); + } + } + + void + unmap() const + { + if (data) { + glUnmapNamedBuffer(buffer); + data = nullptr; + } + } + + glBuffer buffer; + mutable T * data {}; + // Size of buffer + std::size_t capacity {}; + // # used of capacity + std::size_t next {}; + // Index into buffer given to nth proxy + std::vector<size_t> index; + // List of free spaces in index + std::vector<size_t> unused; +}; diff --git a/gfx/gl/sceneRenderer.cpp b/gfx/gl/sceneRenderer.cpp index 873dc5b..6542bea 100644 --- a/gfx/gl/sceneRenderer.cpp +++ b/gfx/gl/sceneRenderer.cpp @@ -18,7 +18,7 @@ SceneRenderer::SceneRenderer(glm::ivec2 s, GLuint o) : lighting {lighting_vs, lighting_fs}, shadowMapper {{2048, 2048}} { shader.setViewPort({0, 0, size.x, size.y}); - VertexArrayObject<glm::i8vec4>::configure(displayVAO, displayVBO, displayVAOdata); + VertexArrayObject {displayVAO}.addAttribs<glm::i8vec4>(displayVBO, displayVAOdata); glBindFramebuffer(GL_FRAMEBUFFER, gBuffer); const auto configuregdata @@ -128,8 +128,8 @@ SceneRenderer::renderQuad() const SceneRenderer::DirectionalLightProgram::DirectionalLightProgram() : Program {lighting_vs, directionalLight_fs}, directionLoc {*this, "lightDirection"}, colourLoc {*this, "lightColour"}, lightViewProjectionLoc {*this, "lightViewProjection"}, - lightViewProjectionCountLoc {*this, "lightViewProjectionCount"}, lightViewShadowMapRegionLoc { - *this, "shadowMapRegion"} + lightViewProjectionCountLoc {*this, "lightViewProjectionCount"}, + lightViewShadowMapRegionLoc {*this, "shadowMapRegion"} { } diff --git a/gfx/gl/sceneShader.cpp b/gfx/gl/sceneShader.cpp index bcd0590..00d9826 100644 --- a/gfx/gl/sceneShader.cpp +++ b/gfx/gl/sceneShader.cpp @@ -8,6 +8,7 @@ #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-pointLight.h> #include <gfx/gl/shaders/vs-spotLight.h> @@ -18,13 +19,17 @@ #include <location.hpp> #include <maths.h> -SceneShader::SceneShader() : landmass {fixedPoint_vs, landmass_fs}, absolute {fixedPoint_vs, material_fs} { } +SceneShader::SceneShader() : + basicInst {dynamicPointInst_vs, material_fs}, landmass {fixedPoint_vs, landmass_fs}, + absolute {fixedPoint_vs, material_fs} +{ +} void SceneShader::setViewProjection(const glm::mat4 & viewProjection) const { - for (const auto & prog : - std::array<const SceneProgram *, 6> {&basic, &water, &landmass, &absolute, &pointLight, &spotLight}) { + for (const auto & prog : std::array<const SceneProgram *, 7> { + &basic, &basicInst, &water, &landmass, &absolute, &pointLight, &spotLight}) { prog->setViewProjection(viewProjection); } } @@ -32,8 +37,8 @@ SceneShader::setViewProjection(const glm::mat4 & viewProjection) const void SceneShader::setViewPort(const glm::ivec4 & viewPort) const { - for (const auto & prog : - std::array<const SceneProgram *, 6> {&basic, &water, &landmass, &absolute, &pointLight, &spotLight}) { + for (const auto & prog : std::array<const SceneProgram *, 7> { + &basic, &basicInst, &water, &landmass, &absolute, &pointLight, &spotLight}) { prog->setViewPort(viewPort); } } @@ -83,7 +88,7 @@ SceneShader::WaterProgram::use(float waveCycle) const SceneShader::PointLightShader::PointLightShader() : SceneProgram {pointLight_vs, pointLight_gs, pointLight_fs}, colourLoc {*this, "colour"}, kqLoc {*this, "kq"} { - VertexArrayObject<glm::vec3>::configure(va, b); + VertexArrayObject {va}.addAttribs<glm::vec3>(b); } void @@ -94,16 +99,16 @@ SceneShader::PointLightShader::add(const glm::vec3 & position, const glm::vec3 & glBindBuffer(GL_ARRAY_BUFFER, b); glUniform3fv(colourLoc, 1, glm::value_ptr(colour)); glUniform1f(kqLoc, kq); - glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(glm::vec3), glm::value_ptr(position)); + glBufferData(GL_ARRAY_BUFFER, sizeof(glm::vec3), glm::value_ptr(position), GL_DYNAMIC_DRAW); glDrawArrays(GL_POINTS, 0, 1); } SceneShader::SpotLightShader::SpotLightShader() : - SceneProgram {spotLight_vs, spotLight_gs, spotLight_fs}, colourLoc {*this, "colour"}, kqLoc {*this, "kq"}, - arcLoc {*this, "arc"} + SceneProgram {spotLight_vs, spotLight_gs, spotLight_fs}, directionLoc {*this, "v_direction"}, + colourLoc {*this, "colour"}, kqLoc {*this, "kq"}, arcLoc {*this, "arc"} { using v3pair = std::pair<glm::vec3, glm::vec3>; - VertexArrayObject<v3pair>::configure<&v3pair::first, &v3pair::second>(va, b); + VertexArrayObject {va}.addAttribs<v3pair, &v3pair::first, &v3pair::second>(b); } void @@ -114,9 +119,9 @@ SceneShader::SpotLightShader::add(const glm::vec3 & position, const glm::vec3 & glBindVertexArray(va); glBindBuffer(GL_ARRAY_BUFFER, b); glUniform3fv(colourLoc, 1, glm::value_ptr(colour)); + glUniform3fv(directionLoc, 1, glm::value_ptr(direction)); glUniform1f(kqLoc, kq); glUniform1f(arcLoc, arc); - glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(glm::vec3), glm::value_ptr(position)); - glBufferSubData(GL_ARRAY_BUFFER, sizeof(glm::vec3), sizeof(glm::vec3), glm::value_ptr(direction)); + glBufferData(GL_ARRAY_BUFFER, sizeof(glm::vec3), glm::value_ptr(position), GL_DYNAMIC_DRAW); glDrawArrays(GL_POINTS, 0, 1); } diff --git a/gfx/gl/sceneShader.h b/gfx/gl/sceneShader.h index ed1bb79..ead184e 100644 --- a/gfx/gl/sceneShader.h +++ b/gfx/gl/sceneShader.h @@ -69,6 +69,7 @@ class SceneShader { const float arc) const; private: + UniformLocation directionLoc; UniformLocation colourLoc; UniformLocation kqLoc; UniformLocation arcLoc; @@ -81,7 +82,7 @@ public: BasicProgram basic; WaterProgram water; - AbsolutePosProgram landmass, absolute; + AbsolutePosProgram basicInst, landmass, absolute; PointLightShader pointLight; SpotLightShader spotLight; diff --git a/gfx/gl/shaders/dynamicPointInst.vs b/gfx/gl/shaders/dynamicPointInst.vs new file mode 100644 index 0000000..1c66979 --- /dev/null +++ b/gfx/gl/shaders/dynamicPointInst.vs @@ -0,0 +1,21 @@ +#version 330 core + +include(`meshIn.glsl') +layout(location = 5) in mat4 model; +include(`materialInterface.glsl') + +uniform mat4 viewProjection; + +void +main() +{ + vec4 worldPos = model * vec4(position, 1.0); + + FragPos = worldPos.xyz; + TexCoords = texCoord; + Normal = (model * vec4(normal, 0.0)).xyz; + Colour = colour; + Material = material; + + gl_Position = viewProjection * worldPos; +} diff --git a/gfx/gl/shaders/spotLight.vs b/gfx/gl/shaders/spotLight.vs index e648553..dca0854 100644 --- a/gfx/gl/shaders/spotLight.vs +++ b/gfx/gl/shaders/spotLight.vs @@ -1,8 +1,8 @@ #version 330 core layout(location = 0) in vec3 v_position; -layout(location = 1) in vec3 v_direction; +uniform vec3 v_direction; uniform vec3 colour; uniform float kq; uniform float arc; diff --git a/gfx/gl/vertexArrayObject.hpp b/gfx/gl/vertexArrayObject.hpp index 5b9fc60..7ded03e 100644 --- a/gfx/gl/vertexArrayObject.hpp +++ b/gfx/gl/vertexArrayObject.hpp @@ -2,50 +2,65 @@ #include "collections.hpp" #include "gl_traits.hpp" +#include "special_members.hpp" #include <GL/glew.h> -#include <glm/common.hpp> -template<typename Vertex> class VertexArrayObject { +class VertexArrayObject { public: - template<auto Vertex::*... attribs> - static void - configure(const GLuint arrayObject, const GLuint arrayBuffer, const GLuint indexBuffer, - const SequentialCollection<Vertex> auto & vertices, const SequentialCollection<unsigned int> auto & indices) + template<typename T> [[nodiscard]] VertexArrayObject(const T & arrayObject) { glBindVertexArray(arrayObject); - - configure_attribs<attribs...>(arrayBuffer); - data(vertices, arrayBuffer, GL_ARRAY_BUFFER); - data(indices, indexBuffer, GL_ELEMENT_ARRAY_BUFFER); - + } + ~VertexArrayObject() + { glBindVertexArray(0); } + NO_MOVE(VertexArrayObject); + NO_COPY(VertexArrayObject); - template<auto Vertex::*... attribs> - static void - configure(const GLuint arrayObject, const GLuint arrayBuffer, const SequentialCollection<Vertex> auto & vertices) - { - glBindVertexArray(arrayObject); + 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>; - configure_attribs<attribs...>(arrayBuffer); + 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); - - glBindVertexArray(0); + return *this; } - template<auto Vertex::*... attribs> - static void - configure(const GLuint arrayObject, const GLuint arrayBuffer) + template<typename VertexT, MP... attribs> + VertexArrayObject & + addAttribs(const GLuint arrayBuffer, const GLuint divisor = 0) { - glBindVertexArray(arrayObject); + configure_attribs<VertexT, attribs...>(arrayBuffer, divisor); + return *this; + } - configure_attribs<attribs...>(arrayBuffer); - glBufferData(GL_ARRAY_BUFFER, static_cast<GLsizeiptr>(sizeof(Vertex)), nullptr, GL_DYNAMIC_DRAW); + template<typename Indices> + VertexArrayObject & + addIndices(const GLuint arrayBuffer, const Indices & indices) + { + data(indices, arrayBuffer, GL_ELEMENT_ARRAY_BUFFER); + return *this; + } - glBindVertexArray(0); + VertexArrayObject & + addIndices(const GLuint arrayBuffer) + { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, arrayBuffer); + return *this; } -private: template<typename Data> static void data(const Data & data, const GLuint arrayBuffer, GLenum target) @@ -55,34 +70,40 @@ private: glBufferData(target, static_cast<GLsizeiptr>(sizeof(Value) * data.size()), data.data(), GL_STATIC_DRAW); } - template<typename T> - static void - set_pointer(const GLuint vertexArrayId, const void * ptr) +private: + template<typename VertexT, typename T> + static auto + set_pointer(const GLuint vertexArrayId, const void * ptr, const GLuint divisor) { - glEnableVertexAttribArray(vertexArrayId); using traits = gl_traits<T>; - traits::vertexAttribFunc(vertexArrayId, traits::size, traits::type, sizeof(Vertex), ptr); + 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<auto Vertex::*attrib> - static void - set_pointer(const GLuint vertexArrayId) + template<typename VertexT, MP attrib> + static auto + set_pointer(const GLuint vertexArrayId, const GLuint divisor) { - set_pointer<std::decay_t<decltype(std::declval<Vertex>().*attrib)>>( - vertexArrayId, &(static_cast<const Vertex *>(nullptr)->*attrib)); + return set_pointer<VertexT, typename decltype(attrib)::value_type>(vertexArrayId, attrib, divisor); } - template<auto Vertex::*... attribs> - static void - configure_attribs(const GLuint arrayBuffer) + template<typename VertexT, MP... attribs> + void + configure_attribs(const GLuint arrayBuffer, const GLuint divisor) { glBindBuffer(GL_ARRAY_BUFFER, arrayBuffer); if constexpr (sizeof...(attribs) == 0) { - set_pointer<Vertex>(0, nullptr); + vertexArrayId += set_pointer<VertexT, VertexT>(vertexArrayId, nullptr, divisor); } else { - GLuint vertexArrayId {}; - (set_pointer<attribs>(vertexArrayId++), ...); + ((vertexArrayId += set_pointer<VertexT, attribs>(vertexArrayId, divisor)), ...); } } + + GLuint vertexArrayId {}; }; diff --git a/gfx/models/mesh.cpp b/gfx/models/mesh.cpp index 2719211..55759cb 100644 --- a/gfx/models/mesh.cpp +++ b/gfx/models/mesh.cpp @@ -7,9 +7,30 @@ Mesh::Mesh(const std::span<const Vertex> vertices, const std::span<const unsigned int> indices, GLenum m) :
m_numIndices {static_cast<GLsizei>(indices.size())}, mode {m}
{
- VertexArrayObject<Vertex>::configure<&Vertex::pos, &Vertex::texCoord, &Vertex::normal, &Vertex::colour,
- &Vertex::material>(
- m_vertexArrayObject, m_vertexArrayBuffers[0], m_vertexArrayBuffers[1], vertices, indices);
+ VertexArrayObject::data(vertices, m_vertexArrayBuffers[0], GL_ARRAY_BUFFER);
+ VertexArrayObject::data(indices, m_vertexArrayBuffers[1], GL_ARRAY_BUFFER);
+ configureVAO(m_vertexArrayObject);
+}
+
+VertexArrayObject &
+Mesh::configureVAO(VertexArrayObject && vao) const
+{
+ return vao
+ .addAttribs<Vertex, &Vertex::pos, &Vertex::texCoord, &Vertex::normal, &Vertex::colour, &Vertex::material>(
+ m_vertexArrayBuffers[0])
+ .addIndices(m_vertexArrayBuffers[1]);
+}
+
+GLsizei
+Mesh::count() const
+{
+ return m_numIndices;
+}
+
+GLenum
+Mesh::type() const
+{
+ return mode;
}
void
diff --git a/gfx/models/mesh.h b/gfx/models/mesh.h index 25a9064..472b7ed 100644 --- a/gfx/models/mesh.h +++ b/gfx/models/mesh.h @@ -7,12 +7,16 @@ #include <stdTypeDefs.hpp>
class Vertex;
+class VertexArrayObject;
class Mesh : public ConstTypeDefs<Mesh> {
public:
Mesh(const std::span<const Vertex> vertices, const std::span<const unsigned int> indices, GLenum = GL_TRIANGLES);
void Draw() const;
+ VertexArrayObject & configureVAO(VertexArrayObject &&) const;
+ GLsizei count() const;
+ GLenum type() const;
private:
glVertexArray m_vertexArrayObject;
diff --git a/lib/gl_traits.hpp b/lib/gl_traits.hpp index e2e689d..6ff8905 100644 --- a/lib/gl_traits.hpp +++ b/lib/gl_traits.hpp @@ -11,20 +11,23 @@ struct gl_traits_base { }; struct gl_traits_float : public gl_traits_base { static constexpr auto vertexAttribFunc { - [](GLuint index, GLint size, GLenum type, GLsizei stride, const void * pointer) { + [](GLuint index, GLint size, GLenum type, GLsizei stride, const void * pointer) -> GLuint { glVertexAttribPointer(index, size, type, GL_FALSE, stride, pointer); + return 1; }}; }; struct gl_traits_longfloat : public gl_traits_base { static constexpr auto vertexAttribFunc { - [](GLuint index, GLint size, GLenum type, GLsizei stride, const void * pointer) { + [](GLuint index, GLint size, GLenum type, GLsizei stride, const void * pointer) -> GLuint { glVertexAttribLPointer(index, size, type, stride, pointer); + return 1; }}; }; struct gl_traits_integer : public gl_traits_base { static constexpr auto vertexAttribFunc { - [](GLuint index, GLint size, GLenum type, GLsizei stride, const void * pointer) { + [](GLuint index, GLint size, GLenum type, GLsizei stride, const void * pointer) -> GLuint { glVertexAttribIPointer(index, size, type, stride, pointer); + return 1; }}; }; template<> struct gl_traits<glm::f32> : public gl_traits_float { @@ -63,4 +66,12 @@ template<glm::length_t L, typename T, glm::qualifier Q> struct gl_traits<glm::ve template<glm::length_t C, glm::length_t R, typename T, glm::qualifier Q> struct gl_traits<glm::mat<C, R, T, Q>> : public gl_traits<T> { static constexpr GLint size {C * R}; + static constexpr auto vertexAttribFunc { + [](GLuint index, GLint, GLenum type, GLsizei stride, const void * pointer) -> GLuint { + const auto base = static_cast<const T *>(pointer); + for (GLuint r = 0; r < R; r++) { + glVertexAttribPointer(index + r, C, type, GL_FALSE, stride, base + (r * C)); + } + return R; + }}; }; diff --git a/test/Jamfile.jam b/test/Jamfile.jam index b0eed5e..1ce73b7 100644 --- a/test/Jamfile.jam +++ b/test/Jamfile.jam @@ -53,10 +53,11 @@ run test-text.cpp ; run test-enumDetails.cpp ; run test-render.cpp : -- : test-assetFactory : <library>test ; run test-glContextBhvr.cpp ; -run test-assetFactory.cpp : -- : [ sequence.insertion-sort [ glob-tree $(res) : *.* ] fixtures/rgb.txt ] : <library>test ; +run test-assetFactory.cpp : -- : [ sequence.insertion-sort [ glob-tree $(res) : *.* ] fixtures/rgb.txt test-instancing ] : <library>test ; run perf-assetFactory.cpp : -- : test-assetFactory : <library>benchmark <library>test ; run perf-persistence.cpp : -- : test-persistence : <library>benchmark <library>test ; run test-worker.cpp ; +run test-instancing.cpp : : : <library>test ; compile test-static-enumDetails.cpp ; compile test-static-stream_support.cpp ; explicit perf-assetFactory ; diff --git a/test/test-assetFactory.cpp b/test/test-assetFactory.cpp index 3d79213..82a1825 100644 --- a/test/test-assetFactory.cpp +++ b/test/test-assetFactory.cpp @@ -29,7 +29,6 @@ public: FactoryFixture() : sceneRenderer {size, output} { } ~FactoryFixture() { - glDisable(GL_DEBUG_OUTPUT); auto outpath = (TMP / boost::unit_test::framework::current_test_case().full_name()).replace_extension(".tga"); std::filesystem::create_directories(outpath.parent_path()); Texture::save(outImage, outpath.c_str()); @@ -108,8 +107,11 @@ BOOST_AUTO_TEST_CASE(foliage, *boost::unit_test::timeout(5)) auto tree_01_1_f = std::dynamic_pointer_cast<Foliage>(tree_01_1); BOOST_REQUIRE(tree_01_1_f); - auto plant = std::make_shared<Plant>(tree_01_1_f, Location {{-2, 2, 0}, {}}); - objects.objects.push_back(plant); + auto plant1 = std::make_shared<Plant>(tree_01_1_f, Location {{-2, 2, 0}, {0, 0, 0}}); + auto plant2 = std::make_shared<Plant>(tree_01_1_f, Location {{3, -4, 0}, {0, 1, 0}}); + auto plant3 = std::make_shared<Plant>(tree_01_1_f, Location {{-2, -4, 0}, {0, 2, 0}}); + auto plant4 = std::make_shared<Plant>(tree_01_1_f, Location {{3, 2, 0}, {0, 3, 0}}); + objects.objects.push_back(tree_01_1_f); render(5); } diff --git a/test/test-instancing.cpp b/test/test-instancing.cpp new file mode 100644 index 0000000..c743ce0 --- /dev/null +++ b/test/test-instancing.cpp @@ -0,0 +1,181 @@ +#define BOOST_TEST_MODULE instancing + +#include "stream_support.hpp" +#include "testHelpers.h" +#include "testMainWindow.h" +#include "ui/applicationBase.h" +#include <boost/test/data/test_case.hpp> +#include <boost/test/unit_test.hpp> +#include <set> + +#include <gfx/gl/instanceVertices.h> + +BOOST_GLOBAL_FIXTURE(ApplicationBase); +BOOST_GLOBAL_FIXTURE(TestMainWindow); + +BOOST_FIXTURE_TEST_SUITE(i, InstanceVertices<int>) + +BOOST_AUTO_TEST_CASE(createDestroy) +{ + BOOST_CHECK(!data); + map(); + BOOST_REQUIRE(data); + BOOST_CHECK_EQUAL(0, next); + BOOST_CHECK(unused.empty()); + BOOST_CHECK(index.empty()); + unmap(); + BOOST_CHECK(!data); +} + +BOOST_AUTO_TEST_CASE(acquireRelease) +{ + { + auto proxy = acquire(); + *proxy = 20; + BOOST_CHECK_EQUAL(1, next); + BOOST_REQUIRE_EQUAL(1, index.size()); + BOOST_CHECK_EQUAL(0, index.front()); + BOOST_CHECK(unused.empty()); + } + BOOST_CHECK_EQUAL(0, next); + BOOST_CHECK(unused.empty()); + BOOST_CHECK(index.empty()); +} + +BOOST_AUTO_TEST_CASE(acquireReleaseMove) +{ + { + auto proxy1 = acquire(); + *proxy1 = 20; + BOOST_CHECK_EQUAL(1, next); + auto proxy2 = std::move(proxy1); + proxy2 = 40; + BOOST_CHECK_EQUAL(1, next); + BOOST_CHECK_EQUAL(data[0], 40); + } + BOOST_CHECK_EQUAL(0, next); + BOOST_CHECK(unused.empty()); + BOOST_CHECK(index.empty()); +} + +BOOST_AUTO_TEST_CASE(autoMapUnmap) +{ + { + auto proxy = acquire(); + BOOST_CHECK(data); + std::ignore = bufferName(); + BOOST_CHECK(data); + BOOST_CHECK_EQUAL(1, count()); + BOOST_CHECK(!data); + } + BOOST_CHECK_EQUAL(0, count()); +} + +BOOST_AUTO_TEST_CASE(initialize) +{ + auto proxy = acquire(5); + const auto & constProxy = proxy; + BOOST_CHECK_EQUAL(*proxy, 5); + BOOST_CHECK_EQUAL(*constProxy, 5); + BOOST_CHECK_EQUAL(constProxy.get(), constProxy.get()); +} + +BOOST_AUTO_TEST_CASE(resize) +{ + constexpr auto COUNT = 500; + std::vector<decltype(acquire())> proxies; + std::vector<int> expected; + for (auto n = 0; n < COUNT; n++) { + proxies.push_back(acquire(n)); + expected.emplace_back(n); + } + BOOST_CHECK_EQUAL_COLLECTIONS(expected.begin(), expected.end(), data, data + COUNT); + BOOST_CHECK_EQUAL_COLLECTIONS(expected.begin(), expected.end(), proxies.begin(), proxies.end()); +} + +BOOST_AUTO_TEST_CASE(shuffle) +{ + std::vector<decltype(acquire())> proxies; + BOOST_CHECK_EQUAL(0, proxies.emplace_back(acquire(0))); + BOOST_CHECK_EQUAL(1, proxies.emplace_back(acquire(1))); + BOOST_CHECK_EQUAL(2, proxies.emplace_back(acquire(2))); + BOOST_CHECK_EQUAL(3, proxies.emplace_back(acquire(3))); + BOOST_CHECK_EQUAL(4, next); + BOOST_CHECK_EQUAL(data + 0, proxies[0].get()); + BOOST_CHECK_EQUAL(data + 1, proxies[1].get()); + BOOST_CHECK_EQUAL(data + 2, proxies[2].get()); + BOOST_CHECK_EQUAL(data + 3, proxies[3].get()); + BOOST_CHECK(unused.empty()); + BOOST_REQUIRE_EQUAL(4, index.size()); + BOOST_CHECK_EQUAL(0, index[0]); + BOOST_CHECK_EQUAL(1, index[1]); + BOOST_CHECK_EQUAL(2, index[2]); + BOOST_CHECK_EQUAL(3, index[3]); + // Remove 1, 3 moves to [1] + proxies.erase(proxies.begin() + 1); + BOOST_REQUIRE_EQUAL(4, index.size()); + BOOST_REQUIRE_EQUAL(1, unused.size()); + BOOST_CHECK_EQUAL(1, unused[0]); + BOOST_CHECK_EQUAL(data + 0, proxies[0].get()); + BOOST_CHECK_EQUAL(data + 2, proxies[1].get()); + BOOST_CHECK_EQUAL(data + 1, proxies[2].get()); + // Remove 1, 2 moves to [1] + proxies.erase(proxies.begin() + 1); + BOOST_REQUIRE_EQUAL(4, index.size()); + BOOST_REQUIRE_EQUAL(2, unused.size()); + BOOST_CHECK_EQUAL(1, unused[0]); + BOOST_CHECK_EQUAL(2, unused[1]); + BOOST_CHECK_EQUAL(data + 0, proxies[0].get()); + BOOST_CHECK_EQUAL(data + 1, proxies[1].get()); + // Add new, takes 2 at [2] + BOOST_CHECK_EQUAL(4, proxies.emplace_back(acquire(4))); + BOOST_REQUIRE_EQUAL(4, index.size()); + BOOST_REQUIRE_EQUAL(1, unused.size()); + BOOST_CHECK_EQUAL(1, unused[0]); + BOOST_CHECK_EQUAL(data + 0, proxies[0].get()); + BOOST_CHECK_EQUAL(data + 1, proxies[1].get()); + BOOST_CHECK_EQUAL(data + 2, proxies[2].get()); +} + +BOOST_DATA_TEST_CASE(shuffle_random, boost::unit_test::data::xrange(0, 10), x) +{ + std::ignore = x; + std::mt19937 gen(std::random_device {}()); + std::map<int, InstanceVertices<int>::InstanceProxy> proxies; + const std::string_view actions = "aaaaaaaarararrraarrrararararaarrrarararararararararraarrrraaaarararaararar"; + int n {}; + for (const auto action : actions) { + switch (action) { + case 'a': + BOOST_REQUIRE_EQUAL(n, proxies.emplace(n, acquire(n)).first->second); + n++; + break; + case 'r': + BOOST_REQUIRE(!proxies.empty()); + auto e = std::next(proxies.begin(), + std::uniform_int_distribution<> {0, static_cast<int>(proxies.size() - 1)}(gen)); + proxies.erase(e); + break; + } + + BOOST_REQUIRE_EQUAL(next, proxies.size()); + for (const auto & [n, p] : proxies) { + BOOST_REQUIRE_EQUAL(n, p); + } + std::set<size_t> iused; + for (size_t i {}; i < index.size(); i++) { + if (std::find(unused.begin(), unused.end(), i) == unused.end()) { + iused.emplace(index[i]); + } + } + BOOST_TEST_CONTEXT(index) { + BOOST_REQUIRE_EQUAL(iused.size(), next); + if (!iused.empty()) { + BOOST_REQUIRE_EQUAL(*iused.begin(), 0); + BOOST_REQUIRE_EQUAL(*iused.rbegin(), next - 1); + } + } + } +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/test/test-render.cpp b/test/test-render.cpp index 45acab5..1643068 100644 --- a/test/test-render.cpp +++ b/test/test-render.cpp @@ -86,7 +86,6 @@ BOOST_AUTO_TEST_CASE(basic) ss.camera.setView({-10, -10, 60}, glm::normalize(glm::vec3 {1, 1, -0.5F})); const TestScene scene; ss.render(scene); - glDisable(GL_DEBUG_OUTPUT); Texture::save(outImage, "/tmp/basic.tga"); } @@ -114,7 +113,6 @@ BOOST_AUTO_TEST_CASE(pointlight) }; const PointLightScene scene; ss.render(scene); - glDisable(GL_DEBUG_OUTPUT); Texture::save(outImage, "/tmp/pointlight.tga"); } @@ -141,7 +139,6 @@ BOOST_AUTO_TEST_CASE(spotlight) }; const PointLightScene scene; ss.render(scene); - glDisable(GL_DEBUG_OUTPUT); Texture::save(outImage, "/tmp/spotlight.tga"); } diff --git a/test/testMainWindow.cpp b/test/testMainWindow.cpp index 49e18f1..57e3473 100644 --- a/test/testMainWindow.cpp +++ b/test/testMainWindow.cpp @@ -12,11 +12,12 @@ TestMainWindow::TestMainWindow() : Window {1, 1, __FILE__, SDL_WINDOW_OPENGL | S (type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : ""), type, severity, message); switch (type) { case GL_DEBUG_TYPE_ERROR: - case GL_DEBUG_TYPE_PERFORMANCE: case GL_DEBUG_TYPE_PORTABILITY: case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR: case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR: BOOST_TEST_ERROR(msg.get()); + case GL_DEBUG_TYPE_PERFORMANCE: + BOOST_TEST_WARN(msg.get()); default: BOOST_TEST_MESSAGE(msg.get()); } diff --git a/ui/gameMainWindow.cpp b/ui/gameMainWindow.cpp index 0b30cad..3f85a4f 100644 --- a/ui/gameMainWindow.cpp +++ b/ui/gameMainWindow.cpp @@ -51,6 +51,11 @@ GameMainWindow::render() const void GameMainWindow::content(const SceneShader & shader) const { + for (const auto & [id, asset] : gameState->assets) { + if (const auto r = std::dynamic_pointer_cast<const Renderable>(asset)) { + r->render(shader); + } + } gameState->world.apply<Renderable>(&Renderable::render, shader); uiComponents.apply<WorldOverlay>(&WorldOverlay::render, shader); } @@ -68,5 +73,10 @@ GameMainWindow::lights(const SceneShader & shader) const void GameMainWindow::shadows(const ShadowMapper & shadowMapper) const { + for (const auto & [id, asset] : gameState->assets) { + if (const auto r = std::dynamic_pointer_cast<const Renderable>(asset)) { + r->shadows(shadowMapper); + } + } gameState->world.apply<Renderable>(&Renderable::shadows, shadowMapper); } |