summaryrefslogtreecommitdiff
path: root/game/terrain.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'game/terrain.cpp')
-rw-r--r--game/terrain.cpp166
1 files changed, 122 insertions, 44 deletions
diff --git a/game/terrain.cpp b/game/terrain.cpp
index 3b16e79..f10aac6 100644
--- a/game/terrain.cpp
+++ b/game/terrain.cpp
@@ -1,65 +1,118 @@
#include "terrain.h"
-#include "game/geoData.h"
-#include "gfx/models/texture.h"
+#include "gfx/frustum.h"
#include <algorithm>
-#include <cstddef>
#include <gfx/gl/sceneShader.h>
#include <gfx/gl/shadowMapper.h>
#include <gfx/image.h>
#include <gfx/models/mesh.h>
#include <gfx/models/vertex.h>
+#include <glMappedBufferWriter.h>
#include <glm/glm.hpp>
-#include <iterator>
#include <location.h>
#include <maths.h>
#include <utility>
#include <vector>
-static constexpr RGB openSurface {-1};
-
-Terrain::Terrain(std::shared_ptr<GeoData> tm) : geoData {std::move(tm)}, grass {std::make_shared<Texture>("grass.png")}
-{
- generateMeshes();
-}
+static constexpr RGB OPEN_SURFACE {-1};
+static constexpr GlobalDistance TILE_SIZE = 1024 * 1024; // ~1km, power of 2, fast divide
template<>
VertexArrayObject &
VertexArrayObject::addAttribsFor<Terrain::Vertex>(const GLuint arrayBuffer, const GLuint divisor)
{
- return addAttribs<Terrain::Vertex, &Terrain::Vertex::pos, &Terrain::Vertex::normal, &Terrain::Vertex::colourBias>(
- arrayBuffer, divisor);
+ return addAttribs<Terrain::Vertex, &Terrain::Vertex::pos, &Terrain::Vertex::normal>(arrayBuffer, divisor);
}
-void
-Terrain::generateMeshes()
+bool
+Terrain::SurfaceKey::operator<(const SurfaceKey & other) const
{
- std::vector<unsigned int> indices;
- indices.reserve(geoData->n_faces() * 3);
- std::vector<Vertex> vertices;
- vertices.reserve(geoData->n_vertices());
- std::map<std::pair<GeoData::VertexHandle, const Surface *>, size_t> vertexIndex;
- std::for_each(geoData->vertices_sbegin(), geoData->vertices_end(),
- [this, &vertexIndex, &vertices](const GeoData::VertexHandle v) {
- std::for_each(geoData->vf_begin(v), geoData->vf_end(v),
- [&vertexIndex, v, this, &vertices](const GeoData::FaceHandle f) {
- const auto surface = geoData->get_surface(f);
- if (const auto vertexIndexRef = vertexIndex.emplace(std::make_pair(v, surface), 0);
- vertexIndexRef.second) {
- vertexIndexRef.first->second = vertices.size();
+ return std::tie(surface, basePosition.x, basePosition.y)
+ < std::tie(other.surface, other.basePosition.x, other.basePosition.y);
+}
- vertices.emplace_back(geoData->point(v), geoData->normal(v),
- surface ? surface->colorBias : openSurface);
- }
- });
- });
- std::for_each(
- geoData->faces_sbegin(), geoData->faces_end(), [this, &vertexIndex, &indices](const GeoData::FaceHandle f) {
- std::transform(geoData->fv_begin(f), geoData->fv_end(f), std::back_inserter(indices),
- [&vertexIndex, f, this](const GeoData::VertexHandle v) {
- return vertexIndex[std::make_pair(v, geoData->get_surface(f))];
- });
+inline void
+Terrain::copyVerticesToBuffer() const
+{
+ std::ranges::transform(all_vertices(), glMappedBufferWriter<Vertex> {GL_ARRAY_BUFFER, verticesBuffer, n_vertices()},
+ [this](const auto & vertex) {
+ return Vertex {point(vertex), normal(vertex)};
});
- meshes.create<MeshT<Vertex>>(vertices, indices);
+}
+
+inline GlobalPosition2D
+Terrain::getTile(const FaceHandle & face) const
+{
+ return point(*cfv_begin(face)).xy() / TILE_SIZE;
+};
+
+Terrain::SurfaceIndices
+Terrain::mapSurfaceFacesToIndices() const
+{
+ SurfaceIndices surfaceIndices;
+ const auto indexBySurfaceAndTile = std::views::transform([this](const auto & faceItr) {
+ return std::pair<SurfaceKey, FaceHandle> {{getSurface(*faceItr), getTile(*faceItr)}, *faceItr};
+ });
+ const auto chunkBySurfaceAndTile = std::views::chunk_by([](const auto & face1, const auto & face2) {
+ return face1.first.surface == face2.first.surface && face1.first.basePosition == face2.first.basePosition;
+ });
+ for (const auto & faceRange : faces() | indexBySurfaceAndTile | chunkBySurfaceAndTile) {
+ const SurfaceKey & surfaceKey = faceRange.front().first;
+ auto indexItr = surfaceIndices.find(surfaceKey);
+ if (indexItr == surfaceIndices.end()) {
+ indexItr = surfaceIndices.emplace(surfaceKey, std::vector<GLuint> {}).first;
+ if (auto existing = meshes.find(surfaceKey); existing != meshes.end()) {
+ indexItr->second.reserve(static_cast<size_t>(existing->second.count));
+ }
+ }
+ for (auto push = std::back_inserter(indexItr->second); const auto & [_, face] : faceRange) {
+ std::ranges::transform(fv_range(face), push, &OpenMesh::VertexHandle::idx);
+ }
+ }
+ return surfaceIndices;
+}
+
+void
+Terrain::copyIndicesToBuffers(const SurfaceIndices & surfaceIndices)
+{
+ for (const auto & [surfaceKey, indices] : surfaceIndices) {
+ auto meshItr = meshes.find(surfaceKey);
+ if (meshItr == meshes.end()) {
+ meshItr = meshes.emplace(surfaceKey, SurfaceArrayBuffer {}).first;
+ VertexArrayObject {meshItr->second.vertexArray}
+ .addAttribsFor<Vertex>(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<GLsizei>(indices.size());
+ meshItr->second.aabb = AxisAlignedBoundingBox<GlobalDistance>::fromPoints(
+ indices | std::views::transform([this](const auto vertex) {
+ return this->point(VertexHandle {static_cast<int>(vertex)});
+ }));
+ }
+}
+
+void
+Terrain::pruneOrphanMeshes(const SurfaceIndices & surfaceIndices)
+{
+ if (meshes.size() > surfaceIndices.size()) {
+ std::erase_if(meshes, [&surfaceIndices](const auto & mesh) {
+ return !surfaceIndices.contains(mesh.first);
+ });
+ }
+}
+
+void
+Terrain::generateMeshes()
+{
+ copyVerticesToBuffer();
+ const auto surfaceIndices = mapSurfaceFacesToIndices();
+ copyIndicesToBuffers(surfaceIndices);
+ pruneOrphanMeshes(surfaceIndices);
}
void
@@ -68,16 +121,41 @@ Terrain::tick(TickDuration)
}
void
-Terrain::render(const SceneShader & shader) const
+Terrain::afterChange()
+{
+ generateMeshes();
+}
+
+void
+Terrain::render(const SceneShader & shader, const Frustum & frustum) const
{
- shader.landmass.use();
grass->bind();
- meshes.apply(&Mesh::Draw);
+
+ const auto chunkBySurface = std::views::chunk_by([](const auto & itr1, const auto & itr2) {
+ return itr1.first.surface == itr2.first.surface;
+ });
+ for (const auto & surfaceRange : meshes | chunkBySurface) {
+ const auto surface = surfaceRange.front().first.surface;
+ shader.landmass.use(surface ? surface->colorBias : OPEN_SURFACE);
+ for (const auto & sab : surfaceRange) {
+ if (frustum.contains(sab.second.aabb)) {
+ glBindVertexArray(sab.second.vertexArray);
+ glDrawElements(GL_TRIANGLES, sab.second.count, GL_UNSIGNED_INT, nullptr);
+ }
+ }
+ }
+ glBindVertexArray(0);
}
void
-Terrain::shadows(const ShadowMapper & shadowMapper) const
+Terrain::shadows(const ShadowMapper & shadowMapper, const Frustum & frustum) const
{
shadowMapper.landmess.use();
- meshes.apply(&Mesh::Draw);
+ for (const auto & [surface, sab] : meshes) {
+ if (frustum.shadedBy(sab.aabb)) {
+ glBindVertexArray(sab.vertexArray);
+ glDrawElements(GL_TRIANGLES, sab.count, GL_UNSIGNED_INT, nullptr);
+ }
+ }
+ glBindVertexArray(0);
}