From df4e70b2c7631317430bd78061507270d23db67b Mon Sep 17 00:00:00 2001 From: DS Date: Thu, 28 Nov 2024 14:22:53 +0100 Subject: [PATCH] Add a setting to group transparency sorted triangles by buffer (#15115) --- builtin/settingtypes.txt | 5 ++++ src/client/clientmap.cpp | 7 ++++- src/client/clientmap.h | 1 + src/client/mapblock_mesh.cpp | 56 +++++++++++++++++++++++++++++------- src/client/mapblock_mesh.h | 10 +++++-- src/defaultsettings.cpp | 1 + 6 files changed, 66 insertions(+), 14 deletions(-) diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt index 418e883d1..d23704694 100644 --- a/builtin/settingtypes.txt +++ b/builtin/settingtypes.txt @@ -1843,6 +1843,11 @@ video_driver (Video driver) enum ,opengl,opengl3,ogles2 # Set to 0 to disable it entirely. transparency_sorting_distance (Transparency Sorting Distance) int 16 0 128 +# Draw transparency sorted triangles grouped by their mesh buffers. +# This breaks transparency sorting between mesh buffers, but avoids situations +# where transparency sorting would be very slow otherwise. +transparency_sorting_group_by_buffers (Transparency Sorting Group by Buffers) bool true + # Radius of cloud area stated in number of 64 node cloud squares. # Values larger than 26 will start to produce sharp cutoffs at cloud area corners. cloud_radius (Cloud radius) int 12 1 62 diff --git a/src/client/clientmap.cpp b/src/client/clientmap.cpp index 9a788e3cc..c3207efd1 100644 --- a/src/client/clientmap.cpp +++ b/src/client/clientmap.cpp @@ -73,6 +73,7 @@ static const std::string ClientMap_settings[] = { "trilinear_filter", "bilinear_filter", "anisotropic_filter", + "transparency_sorting_group_by_buffers", "transparency_sorting_distance", "occlusion_culler", "enable_raytraced_culling", @@ -115,6 +116,9 @@ void ClientMap::onSettingChanged(std::string_view name, bool all) m_cache_bilinear_filter = g_settings->getBool("bilinear_filter"); if (all || name == "anisotropic_filter") m_cache_anistropic_filter = g_settings->getBool("anisotropic_filter"); + if (all || name == "transparency_sorting_group_by_buffers") + m_cache_transparency_sorting_group_by_buffers = + g_settings->getBool("transparency_sorting_group_by_buffers"); if (all || name == "transparency_sorting_distance") m_cache_transparency_sorting_distance = g_settings->getU16("transparency_sorting_distance"); if (all || name == "occlusion_culler") @@ -1337,7 +1341,8 @@ void ClientMap::updateTransparentMeshBuffers() } if (do_sort_block) { - blockmesh->updateTransparentBuffers(m_camera_position, block->getPos()); + blockmesh->updateTransparentBuffers(m_camera_position, block->getPos(), + m_cache_transparency_sorting_group_by_buffers); ++sorted_blocks; } else { blockmesh->consolidateTransparentBuffers(); diff --git a/src/client/clientmap.h b/src/client/clientmap.h index 4855575f6..a72b6f50d 100644 --- a/src/client/clientmap.h +++ b/src/client/clientmap.h @@ -182,6 +182,7 @@ private: bool m_cache_trilinear_filter; bool m_cache_bilinear_filter; bool m_cache_anistropic_filter; + bool m_cache_transparency_sorting_group_by_buffers; u16 m_cache_transparency_sorting_distance; bool m_loops_occlusion_culler; diff --git a/src/client/mapblock_mesh.cpp b/src/client/mapblock_mesh.cpp index c212bd148..c8cd79248 100644 --- a/src/client/mapblock_mesh.cpp +++ b/src/client/mapblock_mesh.cpp @@ -3,6 +3,7 @@ // Copyright (C) 2010-2013 celeron55, Perttu Ahola #include "mapblock_mesh.h" +#include "CMeshBuffer.h" #include "client.h" #include "mapblock.h" #include "map.h" @@ -818,7 +819,8 @@ bool MapBlockMesh::animate(bool faraway, float time, int crack, return true; } -void MapBlockMesh::updateTransparentBuffers(v3f camera_pos, v3s16 block_pos) +void MapBlockMesh::updateTransparentBuffers(v3f camera_pos, v3s16 block_pos, + bool group_by_buffers) { // nothing to do if the entire block is opaque if (m_transparent_triangles.empty()) @@ -834,24 +836,56 @@ void MapBlockMesh::updateTransparentBuffers(v3f camera_pos, v3s16 block_pos) m_transparent_buffers_consolidated = false; m_transparent_buffers.clear(); + std::vector>> ordered_strains; + std::unordered_map strain_idxs; + + if (group_by_buffers) { + // find (reversed) order for strains, by iterating front-to-back + // (if a buffer A has a triangle nearer than all triangles of another + // buffer B, A should be drawn in front of (=after) B) + scene::SMeshBuffer *current_buffer = nullptr; + for (auto it = triangle_refs.rbegin(); it != triangle_refs.rend(); ++it) { + const auto &t = m_transparent_triangles[*it]; + if (current_buffer == t.buffer) + continue; + current_buffer = t.buffer; + auto [_it2, is_new] = + strain_idxs.emplace(current_buffer, ordered_strains.size()); + if (is_new) + ordered_strains.emplace_back(current_buffer, std::vector{}); + } + } + + // find order for triangles, by iterating back-to-front scene::SMeshBuffer *current_buffer = nullptr; - std::vector current_strain; + std::vector *current_strain = nullptr; for (auto i : triangle_refs) { const auto &t = m_transparent_triangles[i]; if (current_buffer != t.buffer) { - if (current_buffer) { - m_transparent_buffers.emplace_back(current_buffer, std::move(current_strain)); - current_strain.clear(); - } current_buffer = t.buffer; + if (group_by_buffers) { + auto it = strain_idxs.find(current_buffer); + assert(it != strain_idxs.end()); + current_strain = &ordered_strains[it->second].second; + } else { + ordered_strains.emplace_back(current_buffer, std::vector{}); + current_strain = &ordered_strains.back().second; + } } - current_strain.push_back(t.p1); - current_strain.push_back(t.p2); - current_strain.push_back(t.p3); + current_strain->push_back(t.p1); + current_strain->push_back(t.p2); + current_strain->push_back(t.p3); } - if (!current_strain.empty()) - m_transparent_buffers.emplace_back(current_buffer, std::move(current_strain)); + m_transparent_buffers.reserve(ordered_strains.size()); + if (group_by_buffers) { + // the order was reversed + for (auto it = ordered_strains.rbegin(); it != ordered_strains.rend(); ++it) + m_transparent_buffers.emplace_back(it->first, std::move(it->second)); + } else { + for (auto it = ordered_strains.begin(); it != ordered_strains.end(); ++it) + m_transparent_buffers.emplace_back(it->first, std::move(it->second)); + } } void MapBlockMesh::consolidateTransparentBuffers() diff --git a/src/client/mapblock_mesh.h b/src/client/mapblock_mesh.h index 5a8daf50c..e7cadb3db 100644 --- a/src/client/mapblock_mesh.h +++ b/src/client/mapblock_mesh.h @@ -209,8 +209,14 @@ public: /// Center of the bounding-sphere, in BS-space, relative to block pos. v3f getBoundingSphereCenter() const { return m_bounding_sphere_center; } - /// update transparent buffers to render towards the camera - void updateTransparentBuffers(v3f camera_pos, v3s16 block_pos); + /** Update transparent buffers to render towards the camera. + * @param group_by_buffers If true, triangles in the same buffer are batched + * into the same PartialMeshBuffer, resulting in fewer draw calls, but + * wrong order. Triangles within a single buffer are still ordered, and + * buffers are ordered relative to each other (with respect to their nearest + * triangle). + */ + void updateTransparentBuffers(v3f camera_pos, v3s16 block_pos, bool group_by_buffers); void consolidateTransparentBuffers(); /// get the list of transparent buffers diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index 345e4a07b..e6b26bd4a 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -302,6 +302,7 @@ void set_default_settings() settings->setDefault("arm_inertia", "true"); settings->setDefault("show_nametag_backgrounds", "true"); settings->setDefault("show_block_bounds_radius_near", "4"); + settings->setDefault("transparency_sorting_group_by_buffers", "true"); settings->setDefault("transparency_sorting_distance", "16"); settings->setDefault("enable_minimap", "true");