diff --git a/src/client/mapblock_mesh.cpp b/src/client/mapblock_mesh.cpp index e50a1f5b78..c4c45d9e24 100644 --- a/src/client/mapblock_mesh.cpp +++ b/src/client/mapblock_mesh.cpp @@ -16,7 +16,9 @@ #include "client/meshgen/collector.h" #include "client/renderingengine.h" #include +#include #include +#include #include "client/texturesource.h" #include #include @@ -585,6 +587,47 @@ void PartialMeshBuffer::draw(video::IVideoDriver *driver) const MapBlockMesh */ +static void applyColorAndMerge(std::vector &prebuffers) +{ + // TODO: we should change the meshgen so it already applies the tile color + // so that we don't need to this extra step. + // However currently the CAO code relies on the ability to erase the vertex + // colors (light data) before applying the tile colors. + + for (auto &p : prebuffers) { + // bake color into vertices + p.applyTileColor(); + // erase color information for later comparisons + p.layer.has_color = false; + p.layer.color = 0; + } + + std::unordered_map seen; + for (size_t i = 0; i < prebuffers.size(); i++) { + PreMeshBuffer &p = prebuffers[i]; + auto it = seen.find(p.layer); + if (it == seen.end()) { // first time + seen[p.layer] = i; + continue; + } + // merge + auto &dst = prebuffers[it->second]; + assert(p.layer == dst.layer); + if (dst.append(p)) { + p = PreMeshBuffer(); + } else { + // other buffer full, this one becomes the new target + it->second = i; + } + } + + // remove all empty buffers + prebuffers.erase(std::remove_if(prebuffers.begin(), prebuffers.end(), + [] (const PreMeshBuffer &p) { + return p.empty(); + }), prebuffers.end()); +} + MapBlockMesh::MapBlockMesh(Client *client, MeshMakeData *data): m_tsrc(client->getTextureSource()), m_shdrsrc(client->getShaderSource()), @@ -638,11 +681,12 @@ MapBlockMesh::MapBlockMesh(Client *client, MeshMakeData *data): for (int layer = 0; layer < MAX_TILE_LAYERS; layer++) { scene::SMesh *mesh = static_cast(m_mesh[layer].get()); - for(u32 i = 0; i < collector.prebuffers[layer].size(); i++) - { - PreMeshBuffer &p = collector.prebuffers[layer][i]; + applyColorAndMerge(collector.prebuffers[layer]); - p.applyTileColor(); + for (size_t i = 0; i < collector.prebuffers[layer].size(); i++) { + PreMeshBuffer &p = collector.prebuffers[layer][i]; + // Note that the buffer index matters, so 'continue' is forbidden here. + assert(!p.empty()); // Generate animation data if (p.layer.material_flags & MATERIAL_FLAG_ANIMATION) { diff --git a/src/client/meshgen/collector.cpp b/src/client/meshgen/collector.cpp index 0c0c73c845..309fd10d12 100644 --- a/src/client/meshgen/collector.cpp +++ b/src/client/meshgen/collector.cpp @@ -4,6 +4,21 @@ #include "collector.h" #include +#include + +bool PreMeshBuffer::append(const PreMeshBuffer &other) +{ + const size_t nv = vertices.size(); + const size_t ni = indices.size(); + if (nv + other.vertices.size() > U16_MAX) + return false; + + vertices.insert(vertices.end(), other.vertices.begin(), other.vertices.end()); + indices.insert(indices.end(), other.indices.begin(), other.indices.end()); + for (size_t i = ni; i < indices.size(); i++) + indices[i] += nv; + return true; +} void MeshCollector::append(const TileSpec &tile, const video::S3DVertex *vertices, u32 numVertices, const u16 *indices, u32 numIndices) @@ -24,6 +39,7 @@ void MeshCollector::append(const TileLayer &layer, const video::S3DVertex *verti const u16 aux = layer.texture_layer_idx; u32 vertex_count = p.vertices.size(); + assert(vertex_count + numVertices <= U16_MAX); for (u32 i = 0; i < numVertices; i++) { p.vertices.emplace_back(vertices[i].Pos + offset, vertices[i].Normal, vertices[i].Color, vertices[i].TCoords, aux); diff --git a/src/client/meshgen/collector.h b/src/client/meshgen/collector.h index 876338baa9..1a1b3fa453 100644 --- a/src/client/meshgen/collector.h +++ b/src/client/meshgen/collector.h @@ -19,6 +19,10 @@ struct PreMeshBuffer PreMeshBuffer() = default; explicit PreMeshBuffer(const TileLayer &layer) : layer(layer) {} + bool empty() const { + return indices.empty(); + } + /// @brief Colorizes vertices as indicated by tile layer void applyTileColor() { @@ -33,6 +37,10 @@ struct PreMeshBuffer c->getBlue() * tc.getBlue() / 255U); } } + + /// @brief Append another buffer to this one + /// @return false if index would overflow + bool append(const PreMeshBuffer &other); }; struct MeshCollector diff --git a/src/client/tile.h b/src/client/tile.h index 5ec61c3c72..46fc57a7c1 100644 --- a/src/client/tile.h +++ b/src/client/tile.h @@ -167,6 +167,23 @@ struct TileLayer bool has_color = false; }; +template<> +struct std::hash +{ + // All layers equal according to TileLayer::operator== will have the same + // hash value according to this function. + std::size_t operator()(const TileLayer &l) const noexcept + { + std::size_t ret = 0; + for (auto h : { l.texture_id, l.shader_id, (u32)l.material_flags }) { + ret += h; + ret ^= (ret << 6) + (ret >> 2); // distribute bits + } + return ret; + } +}; + + // Stores information for drawing an animated tile struct AnimationInfo {