From f9962b0db5072870864a1f758687106139f740dc Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Tue, 25 Feb 2025 00:23:11 +0000 Subject: Improve Terrain::generateMeshes performance Creates mesh per surface based on a single buffer of all vertices and a collection of per surfuce indices. Makes ColourBias a uniform instead of a vertex attribute. --- game/terrain.cpp | 79 +++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 50 insertions(+), 29 deletions(-) (limited to 'game/terrain.cpp') diff --git a/game/terrain.cpp b/game/terrain.cpp index 01af163..c28f7ee 100644 --- a/game/terrain.cpp +++ b/game/terrain.cpp @@ -1,12 +1,11 @@ #include "terrain.h" -#include "game/geoData.h" #include -#include #include #include #include #include #include +#include #include #include #include @@ -20,37 +19,51 @@ template<> VertexArrayObject & VertexArrayObject::addAttribsFor(const GLuint arrayBuffer, const GLuint divisor) { - return addAttribs( - arrayBuffer, divisor); + return addAttribs(arrayBuffer, divisor); } void Terrain::generateMeshes() { - meshes.removeAll(); - std::vector indices; - indices.reserve(n_faces() * 3); - std::vector vertices; - vertices.reserve(n_vertices()); - std::map, size_t> vertexIndex; - std::ranges::for_each(this->vertices(), [this, &vertexIndex, &vertices](const auto vertex) { - std::ranges::for_each(vf_range(vertex), [&vertexIndex, vertex, this, &vertices](const auto face) { - const auto * const surface = getSurface(face); - if (const auto vertexIndexRef = vertexIndex.emplace(std::make_pair(vertex, surface), 0); - vertexIndexRef.second) { - vertexIndexRef.first->second = vertices.size(); + std::ranges::transform(all_vertices(), glMappedBufferWriter {GL_ARRAY_BUFFER, verticesBuffer, n_vertices()}, + [this](const auto & vertex) { + return Vertex {point(vertex), normal(vertex)}; + }); - vertices.emplace_back(point(vertex), normal(vertex), surface ? surface->colorBias : OPEN_SURFACE); + std::map> surfaceIndices; + for (const auto face : faces()) { + const auto * const surface = getSurface(face); + auto indexItr = surfaceIndices.find(surface); + if (indexItr == surfaceIndices.end()) { + indexItr = surfaceIndices.emplace(surface, std::vector {}).first; + if (!surface) { + indexItr->second.reserve(n_vertices() * 3); } + } + std::ranges::transform(fv_range(face), std::back_inserter(indexItr->second), &OpenMesh::VertexHandle::idx); + } + + for (const auto & [surface, indices] : surfaceIndices) { + auto meshItr = meshes.find(surface); + if (meshItr == meshes.end()) { + meshItr = meshes.emplace(surface, SurfaceArrayBuffer {}); + VertexArrayObject {meshItr->second.vertexArray} + .addAttribsFor(verticesBuffer) + .addIndices(meshItr->second.indicesBuffer, indices) + .data(verticesBuffer, GL_ARRAY_BUFFER); + } + else { + VertexArrayObject {meshItr->second.vertexArray} + .addIndices(meshItr->second.indicesBuffer, indices) + .data(verticesBuffer, GL_ARRAY_BUFFER); + } + meshItr->second.count = static_cast(indices.size()); + } + if (meshes.size() > surfaceIndices.size()) { + std::erase_if(meshes, [&surfaceIndices](const auto & mesh) { + return !surfaceIndices.contains(mesh.first); }); - }); - std::ranges::for_each(faces(), [this, &vertexIndex, &indices](const auto face) { - std::ranges::transform( - fv_range(face), std::back_inserter(indices), [&vertexIndex, face, this](const auto vertex) { - return vertexIndex[std::make_pair(vertex, getSurface(face))]; - }); - }); - meshes.create>(vertices, indices); + } } void @@ -61,20 +74,28 @@ Terrain::tick(TickDuration) void Terrain::afterChange() { - generateMeshes(); + generateMeshes(); } void Terrain::render(const SceneShader & shader) const { - shader.landmass.use(); grass->bind(); - meshes.apply(&Mesh::Draw); + for (const auto & [surface, sab] : meshes) { + shader.landmass.use(surface ? surface->colorBias : OPEN_SURFACE); + glBindVertexArray(sab.vertexArray); + glDrawElements(GL_TRIANGLES, sab.count, GL_UNSIGNED_INT, nullptr); + } + glBindVertexArray(0); } void Terrain::shadows(const ShadowMapper & shadowMapper) const { shadowMapper.landmess.use(); - meshes.apply(&Mesh::Draw); + for (const auto & [surface, sab] : meshes) { + glBindVertexArray(sab.vertexArray); + glDrawElements(GL_TRIANGLES, sab.count, GL_UNSIGNED_INT, nullptr); + } + glBindVertexArray(0); } -- cgit v1.2.3