From e0b051346567a0ee1c005643655b3042b7ff5544 Mon Sep 17 00:00:00 2001 From: Vitaliy Date: Fri, 25 Feb 2022 19:23:03 +0300 Subject: [PATCH] Transparency sorting for DrawList and Materials (#41) --- builtin/settingtypes.txt | 5 +++++ multicraft.conf.example | 6 ++++++ src/client/clientmap.cpp | 36 +++++++++++++++++++++++------------- src/client/clientmap.h | 8 +++++++- src/defaultsettings.cpp | 1 + src/util/numeric.cpp | 6 +++--- 6 files changed, 45 insertions(+), 17 deletions(-) diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt index aff93227b..c6b7882f8 100644 --- a/builtin/settingtypes.txt +++ b/builtin/settingtypes.txt @@ -805,6 +805,11 @@ autoscale_mode (Autoscaling mode) enum disable disable,enable,force # A restart is required after changing this. show_entity_selectionbox (Show entity selection boxes) bool false +# Sort transparent materials from back to front for proper rendering +# Note: works on a per-chunk basis +# Note: different materials are sorted independently +transparency_sorting (Transparency sorting) bool true + [*Menus] # Use a cloud animation for the main menu background. diff --git a/multicraft.conf.example b/multicraft.conf.example index ed78a2036..55703401f 100644 --- a/multicraft.conf.example +++ b/multicraft.conf.example @@ -939,6 +939,12 @@ # type: bool # show_entity_selectionbox = false +# Sort transparent materials from back to front for proper rendering +# Note: works on a per-chunk basis +# Note: different materials are sorted independently +# type: bool +# transparency_sorting = true + ## Menus # Use a cloud animation for the main menu background. diff --git a/src/client/clientmap.cpp b/src/client/clientmap.cpp index 7becf8b1f..15b9101e7 100644 --- a/src/client/clientmap.cpp +++ b/src/client/clientmap.cpp @@ -88,6 +88,7 @@ ClientMap::ClientMap( m_cache_trilinear_filter = g_settings->getBool("trilinear_filter"); m_cache_bilinear_filter = g_settings->getBool("bilinear_filter"); m_cache_anistropic_filter = g_settings->getBool("anisotropic_filter"); + m_cache_transparency_sorting = g_settings->getFlag("transparency_sorting"); } @@ -147,8 +148,8 @@ void ClientMap::updateDrawList() { ScopeProfiler sp(g_profiler, "CM::updateDrawList()", SPT_AVG); - for (auto &i : m_drawlist) { - MapBlock *block = i.second; + for (auto const &i : m_drawlist) { + MapBlock *block = i.block; block->refDrop(); } m_drawlist.clear(); @@ -247,7 +248,7 @@ void ClientMap::updateDrawList() // Add to set block->refGrab(); - m_drawlist[block->getPos()] = block; + m_drawlist.push_back({block, d}); sector_blocks_drawn++; } // foreach sectorblocks @@ -256,6 +257,12 @@ void ClientMap::updateDrawList() m_last_drawn_sectors.insert(sp); } + if (m_drawlist.capacity() > m_drawlist.size() / 4) + m_drawlist.shrink_to_fit(); + + if (m_cache_transparency_sorting) + std::sort(m_drawlist.begin(), m_drawlist.end(), [] (DrawListItem const &a, DrawListItem const &b) { return a.distance > b.distance; }); + g_profiler->avg("MapBlock meshes in range [#]", blocks_in_range_with_mesh); g_profiler->avg("MapBlocks occlusion culled [#]", blocks_occlusion_culled); g_profiler->avg("MapBlocks drawn [#]", m_drawlist.size()); @@ -306,16 +313,11 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) MeshBufListList drawbufs; - for (auto &i : m_drawlist) { - v3s16 block_pos = i.first; - MapBlock *block = i.second; - - // If the mesh of the block happened to get deleted, ignore it - if (!block->mesh) - continue; - - float d = 0.0; - if (!isBlockInSight(block->getPos(), camera_position, + for (auto &item : m_drawlist) { + MapBlock *block = item.block; + v3s16 block_pos = block->getPos(); + float d; + if (!isBlockInSight(block_pos, camera_position, camera_direction, camera_fov, 100000 * BS, &d)) continue; @@ -387,6 +389,14 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) // Render all layers in order for (auto &lists : drawbufs.lists) { + if (m_cache_transparency_sorting) { + static const auto comparator = [] (MeshBufList const &a, MeshBufList const &b) { + // comparing pointers with < is UB unless they belong to the same array, but std::less is always allowed + static const std::less texture_less; + return texture_less(a.m.TextureLayer[0].Texture, b.m.TextureLayer[0].Texture); + }; + std::sort(lists.begin(), lists.end(), comparator); + } for (MeshBufList &list : lists) { // Check and abort if the machine is swapping a lot if (draw.getTimerTime() > 2000) { diff --git a/src/client/clientmap.h b/src/client/clientmap.h index 9826f86db..3a4c2c054 100644 --- a/src/client/clientmap.h +++ b/src/client/clientmap.h @@ -55,6 +55,11 @@ struct MeshBufListList void add(scene::IMeshBuffer *buf, v3s16 position, u8 layer); }; +struct DrawListItem { + MapBlock *block; + float distance; +}; + class Client; class ITextureSource; @@ -146,11 +151,12 @@ private: f32 m_camera_fov = M_PI; v3s16 m_camera_offset; - std::map m_drawlist; + std::vector m_drawlist; std::set m_last_drawn_sectors; bool m_cache_trilinear_filter; bool m_cache_bilinear_filter; bool m_cache_anistropic_filter; + bool m_cache_transparency_sorting; }; diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index 2569e96c7..a1967515b 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -245,6 +245,7 @@ void set_default_settings() settings->setDefault("round_screen", "0"); settings->setDefault("enable_local_map_saving", "false"); settings->setDefault("show_entity_selectionbox", "false"); + settings->setDefault("transparency_sorting", "true"); settings->setDefault("texture_clean_transparent", "false"); settings->setDefault("texture_min_size", "0"); settings->setDefault("ambient_occlusion_gamma", "2.2"); diff --git a/src/util/numeric.cpp b/src/util/numeric.cpp index a95232042..873116f8e 100644 --- a/src/util/numeric.cpp +++ b/src/util/numeric.cpp @@ -123,18 +123,18 @@ bool isBlockInSight(v3s16 blockpos_b, v3f camera_pos, v3f camera_dir, v3f blockpos_relative = blockpos - camera_pos; // Total distance - f32 d = MYMAX(0, blockpos_relative.getLength() - block_max_radius); + f32 d = blockpos_relative.getLength(); if (distance_ptr) *distance_ptr = d; // If block is far away, it's not in sight - if (d > range) + if (d > range + block_max_radius) return false; // If block is (nearly) touching the camera, don't // bother validating further (that is, render it anyway) - if (d == 0) + if (d <= block_max_radius) return true; // Adjust camera position, for purposes of computing the angle,