#include "terrain.h" #include #include #include #include #include #include #include #include #include #include #include #include static constexpr RGB OPEN_SURFACE {-1}; template<> VertexArrayObject & VertexArrayObject::addAttribsFor(const GLuint arrayBuffer, const GLuint divisor) { return addAttribs(arrayBuffer, divisor); } bool Terrain::SurfaceKey::operator<(const SurfaceKey & other) const { return std::tie(surface, basePosition.x, basePosition.y) < std::tie(other.surface, other.basePosition.x, other.basePosition.y); } void Terrain::generateMeshes() { constexpr GlobalDistance TILE_SIZE = 1024 * 1024; // ~1km, power of 2, fast divide std::ranges::transform(all_vertices(), glMappedBufferWriter {GL_ARRAY_BUFFER, verticesBuffer, n_vertices()}, [this](const auto & vertex) { return Vertex {point(vertex), normal(vertex)}; }); std::map> surfaceIndices; const auto getTile = [this](FaceHandle face) { return point(*fv_begin(face)).xy() / TILE_SIZE; }; const auto indexBySurfaceAndTile = std::views::transform([this, &getTile](const auto & faceItr) { return std::pair {{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 {}).first; if (auto existing = meshes.find(surfaceKey); existing != meshes.end()) { indexItr->second.reserve(static_cast(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); } } 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(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); }); } } void Terrain::tick(TickDuration) { } void Terrain::afterChange() { generateMeshes(); } void Terrain::render(const SceneShader & shader) const { grass->bind(); 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) { glBindVertexArray(sab.second.vertexArray); glDrawElements(GL_TRIANGLES, sab.second.count, GL_UNSIGNED_INT, nullptr); } } glBindVertexArray(0); } void Terrain::shadows(const ShadowMapper & shadowMapper) const { shadowMapper.landmess.use(); for (const auto & [surface, sab] : meshes) { glBindVertexArray(sab.vertexArray); glDrawElements(GL_TRIANGLES, sab.count, GL_UNSIGNED_INT, nullptr); } glBindVertexArray(0); }