]> git.lizzy.rs Git - dragonfireclient.git/blobdiff - src/client/clientmap.cpp
Merge branch 'master' of https://github.com/minetest/minetest
[dragonfireclient.git] / src / client / clientmap.cpp
index 4a4784f91b13e575c1b6aef9299fcc0d0cec6113..51f0f6896774dc10a64cb622c0b5499f492a5586 100644 (file)
@@ -73,7 +73,8 @@ ClientMap::ClientMap(
                rendering_engine->get_scene_manager(), id),
        m_client(client),
        m_rendering_engine(rendering_engine),
-       m_control(control)
+       m_control(control),
+       m_drawlist(MapBlockComparer(v3s16(0,0,0)))
 {
 
        /*
@@ -96,9 +97,32 @@ 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_distance = g_settings->getU16("transparency_sorting_distance");
 
 }
 
+void ClientMap::updateCamera(v3f pos, v3f dir, f32 fov, v3s16 offset)
+{
+       v3s16 previous_node = floatToInt(m_camera_position, BS) + m_camera_offset;
+       v3s16 previous_block = getContainerPos(previous_node, MAP_BLOCKSIZE);
+
+       m_camera_position = pos;
+       m_camera_direction = dir;
+       m_camera_fov = fov;
+       m_camera_offset = offset;
+
+       v3s16 current_node = floatToInt(m_camera_position, BS) + m_camera_offset;
+       v3s16 current_block = getContainerPos(current_node, MAP_BLOCKSIZE);
+
+       // reorder the blocks when camera crosses block boundary
+       if (previous_block != current_block)
+               m_needs_update_drawlist = true;
+
+       // reorder transparent meshes when camera crosses node boundary
+       if (previous_node != current_node)
+               m_needs_update_transparent_meshes = true;
+}
+
 MapSector * ClientMap::emergeSector(v2s16 p2d)
 {
        // Check that it doesn't exist already
@@ -164,6 +188,8 @@ void ClientMap::updateDrawList()
 {
        ScopeProfiler sp(g_profiler, "CM::updateDrawList()", SPT_AVG);
 
+       m_needs_update_drawlist = false;
+
        for (auto &i : m_drawlist) {
                MapBlock *block = i.second;
                block->refDrop();
@@ -178,6 +204,7 @@ void ClientMap::updateDrawList()
        const f32 camera_fov = m_camera_fov * 1.1f;
 
        v3s16 cam_pos_nodes = floatToInt(camera_position, BS);
+
        v3s16 p_blocks_min;
        v3s16 p_blocks_max;
        getBlocksInViewRange(cam_pos_nodes, &p_blocks_min, &p_blocks_max);
@@ -202,6 +229,8 @@ void ClientMap::updateDrawList()
                        occlusion_culling_enabled = false;
        }
 
+       v3s16 camera_block = getContainerPos(cam_pos_nodes, MAP_BLOCKSIZE);
+       m_drawlist = std::map<v3s16, MapBlock*, MapBlockComparer>(MapBlockComparer(camera_block));
 
        // Uncomment to debug occluded blocks in the wireframe mode
        // TODO: Include this as a flag for an extended debugging setting
@@ -317,11 +346,19 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
        u32 mesh_animate_count = 0;
        //u32 mesh_animate_count_far = 0;
 
+       /*
+               Update transparent meshes
+       */
+       if (is_transparent_pass)
+               updateTransparentMeshBuffers();
+
        /*
                Draw the selected MapBlocks
        */
 
-       MeshBufListList drawbufs;
+       MeshBufListList grouped_buffers;
+       std::vector<DrawDescriptor> draw_order;
+       video::SMaterial previous_material;
 
        for (auto &i : m_drawlist) {
                v3s16 block_pos = i.first;
@@ -356,7 +393,15 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
                /*
                        Get the meshbuffers of the block
                */
-               {
+               if (is_transparent_pass) {
+                       // In transparent pass, the mesh will give us
+                       // the partial buffers in the correct order
+                       for (auto &buffer : block->mesh->getTransparentBuffers())
+                               draw_order.emplace_back(block_pos, &buffer);
+               }
+               else {
+                       // otherwise, group buffers across meshes
+                       // using MeshBufListList
                        MapBlockMesh *mapBlockMesh = block->mesh;
                        assert(mapBlockMesh);
 
@@ -370,69 +415,94 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
 
                                        video::SMaterial& material = buf->getMaterial();
                                        video::IMaterialRenderer* rnd =
-                                               driver->getMaterialRenderer(material.MaterialType);
+                                                       driver->getMaterialRenderer(material.MaterialType);
                                        bool transparent = (rnd && rnd->isTransparent());
-                                       if (transparent == is_transparent_pass) {
+                                       if (!transparent) {
                                                if (buf->getVertexCount() == 0)
                                                        errorstream << "Block [" << analyze_block(block)
-                                                               << "] contains an empty meshbuf" << std::endl;
-
-                                               material.setFlag(video::EMF_TRILINEAR_FILTER,
-                                                       m_cache_trilinear_filter);
-                                               material.setFlag(video::EMF_BILINEAR_FILTER,
-                                                       m_cache_bilinear_filter);
-                                               material.setFlag(video::EMF_ANISOTROPIC_FILTER,
-                                                       m_cache_anistropic_filter);
-                                               material.setFlag(video::EMF_WIREFRAME,
-                                                       m_control.show_wireframe);
-
-                                               drawbufs.add(buf, block_pos, layer);
+                                                                       << "] contains an empty meshbuf" << std::endl;
+
+                                               grouped_buffers.add(buf, block_pos, layer);
                                        }
                                }
                        }
                }
        }
 
+       // Capture draw order for all solid meshes
+       for (auto &lists : grouped_buffers.lists) {
+               for (MeshBufList &list : lists) {
+                       // iterate in reverse to draw closest blocks first
+                       for (auto it = list.bufs.rbegin(); it != list.bufs.rend(); ++it) {
+                               draw_order.emplace_back(it->first, it->second, it != list.bufs.rbegin());
+                       }
+               }
+       }
+
        TimeTaker draw("Drawing mesh buffers");
 
        core::matrix4 m; // Model matrix
        v3f offset = intToFloat(m_camera_offset, BS);
+       u32 material_swaps = 0;
 
-       // Render all layers in order
-       for (auto &lists : drawbufs.lists) {
-               for (MeshBufList &list : lists) {
-                       // Check and abort if the machine is swapping a lot
-                       if (draw.getTimerTime() > 2000) {
-                               infostream << "ClientMap::renderMap(): Rendering took >2s, " <<
-                                               "returning." << std::endl;
-                               return;
-                       }
+       // Render all mesh buffers in order
+       drawcall_count += draw_order.size();
+
+       for (auto &descriptor : draw_order) {
+               scene::IMeshBuffer *buf;
+               
+               if (descriptor.m_use_partial_buffer) {
+                       descriptor.m_partial_buffer->beforeDraw();
+                       buf = descriptor.m_partial_buffer->getBuffer();
+               }
+               else {
+                       buf = descriptor.m_buffer;
+               }
+
+               // Check and abort if the machine is swapping a lot
+               if (draw.getTimerTime() > 2000) {
+                       infostream << "ClientMap::renderMap(): Rendering took >2s, " <<
+                                       "returning." << std::endl;
+                       return;
+               }
+
+               if (!descriptor.m_reuse_material) {
+                       auto &material = buf->getMaterial();
+
+                       // Apply filter settings
+                       material.setFlag(video::EMF_TRILINEAR_FILTER,
+                               m_cache_trilinear_filter);
+                       material.setFlag(video::EMF_BILINEAR_FILTER,
+                               m_cache_bilinear_filter);
+                       material.setFlag(video::EMF_ANISOTROPIC_FILTER,
+                               m_cache_anistropic_filter);
+                       material.setFlag(video::EMF_WIREFRAME,
+                               m_control.show_wireframe);
 
                        // pass the shadow map texture to the buffer texture
                        ShadowRenderer *shadow = m_rendering_engine->get_shadow_renderer();
                        if (shadow && shadow->is_active()) {
-                               auto &layer = list.m.TextureLayer[3];
+                               auto &layer = material.TextureLayer[3];
                                layer.Texture = shadow->get_texture();
                                layer.TextureWrapU = video::E_TEXTURE_CLAMP::ETC_CLAMP_TO_EDGE;
                                layer.TextureWrapV = video::E_TEXTURE_CLAMP::ETC_CLAMP_TO_EDGE;
-                               layer.TrilinearFilter = true;
+                               // Do not enable filter on shadow texture to avoid visual artifacts
+                               // with colored shadows.
+                               // Filtering is done in shader code anyway
+                               layer.TrilinearFilter = false;
                        }
+                       driver->setMaterial(material);
+                       ++material_swaps;
+               }
 
-                       driver->setMaterial(list.m);
-
-                       drawcall_count += list.bufs.size();
-                       for (auto &pair : list.bufs) {
-                               scene::IMeshBuffer *buf = pair.second;
-
-                               v3f block_wpos = intToFloat(pair.first * MAP_BLOCKSIZE, BS);
-                               m.setTranslation(block_wpos - offset);
+               v3f block_wpos = intToFloat(descriptor.m_pos * MAP_BLOCKSIZE, BS);
+               m.setTranslation(block_wpos - offset);
 
-                               driver->setTransform(video::ETS_WORLD, m);
-                               driver->drawMeshBuffer(buf);
-                               vertex_count += buf->getVertexCount();
-                       }
-               }
+               driver->setTransform(video::ETS_WORLD, m);
+               driver->drawMeshBuffer(buf);
+               vertex_count += buf->getIndexCount();
        }
+
        g_profiler->avg(prefix + "draw meshes [ms]", draw.stop(true));
 
        // Log only on solid pass because values are the same
@@ -440,8 +510,13 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
                g_profiler->avg("renderMap(): animated meshes [#]", mesh_animate_count);
        }
 
+       if (pass == scene::ESNRP_TRANSPARENT) {
+               g_profiler->avg("renderMap(): transparent buffers [#]", draw_order.size());
+       }
+
        g_profiler->avg(prefix + "vertices drawn [#]", vertex_count);
        g_profiler->avg(prefix + "drawcalls [#]", drawcall_count);
+       g_profiler->avg(prefix + "material swaps [#]", material_swaps);
 }
 
 static bool getVisibleBrightness(Map *map, const v3f &p0, v3f dir, float step,
@@ -648,7 +723,9 @@ void ClientMap::renderMapShadows(video::IVideoDriver *driver,
        u32 drawcall_count = 0;
        u32 vertex_count = 0;
 
-       MeshBufListList drawbufs;
+       MeshBufListList grouped_buffers;
+       std::vector<DrawDescriptor> draw_order;
+
 
        int count = 0;
        int low_bound = is_transparent_pass ? 0 : m_drawlist_shadow.size() / total_frames * frame;
@@ -677,7 +754,15 @@ void ClientMap::renderMapShadows(video::IVideoDriver *driver,
                /*
                        Get the meshbuffers of the block
                */
-               {
+               if (is_transparent_pass) {
+                       // In transparent pass, the mesh will give us
+                       // the partial buffers in the correct order
+                       for (auto &buffer : block->mesh->getTransparentBuffers())
+                               draw_order.emplace_back(block_pos, &buffer);
+               }
+               else {
+                       // otherwise, group buffers across meshes
+                       // using MeshBufListList
                        MapBlockMesh *mapBlockMesh = block->mesh;
                        assert(mapBlockMesh);
 
@@ -692,79 +777,99 @@ void ClientMap::renderMapShadows(video::IVideoDriver *driver,
                                        video::SMaterial &mat = buf->getMaterial();
                                        auto rnd = driver->getMaterialRenderer(mat.MaterialType);
                                        bool transparent = rnd && rnd->isTransparent();
-                                       if (transparent == is_transparent_pass)
-                                               drawbufs.add(buf, block_pos, layer);
+                                       if (!transparent)
+                                               grouped_buffers.add(buf, block_pos, layer);
                                }
                        }
                }
        }
 
+       u32 buffer_count = 0;
+       for (auto &lists : grouped_buffers.lists)
+               for (MeshBufList &list : lists)
+                       buffer_count += list.bufs.size();
+       
+       draw_order.reserve(draw_order.size() + buffer_count);
+       
+       // Capture draw order for all solid meshes
+       for (auto &lists : grouped_buffers.lists) {
+               for (MeshBufList &list : lists) {
+                       // iterate in reverse to draw closest blocks first
+                       for (auto it = list.bufs.rbegin(); it != list.bufs.rend(); ++it)
+                               draw_order.emplace_back(it->first, it->second, it != list.bufs.rbegin());
+               }
+       }
+
        TimeTaker draw("Drawing shadow mesh buffers");
 
        core::matrix4 m; // Model matrix
        v3f offset = intToFloat(m_camera_offset, BS);
+       u32 material_swaps = 0;
 
-       // Render all layers in order
-       for (auto &lists : drawbufs.lists) {
-               for (MeshBufList &list : lists) {
-                       // Check and abort if the machine is swapping a lot
-                       if (draw.getTimerTime() > 1000) {
-                               infostream << "ClientMap::renderMapShadows(): Rendering "
-                                               "took >1s, returning." << std::endl;
-                               break;
-                       }
-                       for (auto &pair : list.bufs) {
-                               scene::IMeshBuffer *buf = pair.second;
-
-                               // override some material properties
-                               video::SMaterial local_material = buf->getMaterial();
-                               local_material.MaterialType = material.MaterialType;
-                               local_material.BackfaceCulling = material.BackfaceCulling;
-                               local_material.FrontfaceCulling = material.FrontfaceCulling;
-                               local_material.BlendOperation = material.BlendOperation;
-                               local_material.Lighting = false;
-                               driver->setMaterial(local_material);
-
-                               v3f block_wpos = intToFloat(pair.first * MAP_BLOCKSIZE, BS);
-                               m.setTranslation(block_wpos - offset);
-
-                               driver->setTransform(video::ETS_WORLD, m);
-                               driver->drawMeshBuffer(buf);
-                               vertex_count += buf->getVertexCount();
-                       }
+       // Render all mesh buffers in order
+       drawcall_count += draw_order.size();
+
+       for (auto &descriptor : draw_order) {
+               scene::IMeshBuffer *buf;
+               
+               if (descriptor.m_use_partial_buffer) {
+                       descriptor.m_partial_buffer->beforeDraw();
+                       buf = descriptor.m_partial_buffer->getBuffer();
+               }
+               else {
+                       buf = descriptor.m_buffer;
+               }
+
+               // Check and abort if the machine is swapping a lot
+               if (draw.getTimerTime() > 1000) {
+                       infostream << "ClientMap::renderMapShadows(): Rendering "
+                                       "took >1s, returning." << std::endl;
+                       break;
+               }
 
-                       drawcall_count += list.bufs.size();
+               if (!descriptor.m_reuse_material) {
+                       // override some material properties
+                       video::SMaterial local_material = buf->getMaterial();
+                       local_material.MaterialType = material.MaterialType;
+                       local_material.BackfaceCulling = material.BackfaceCulling;
+                       local_material.FrontfaceCulling = material.FrontfaceCulling;
+                       local_material.BlendOperation = material.BlendOperation;
+                       local_material.Lighting = false;
+                       driver->setMaterial(local_material);
+                       ++material_swaps;
                }
+
+               v3f block_wpos = intToFloat(descriptor.m_pos * MAP_BLOCKSIZE, BS);
+               m.setTranslation(block_wpos - offset);
+
+               driver->setTransform(video::ETS_WORLD, m);
+               driver->drawMeshBuffer(buf);
+               vertex_count += buf->getIndexCount();
        }
 
-       // restore the driver material state 
+       // restore the driver material state
        video::SMaterial clean;
        clean.BlendOperation = video::EBO_ADD;
        driver->setMaterial(clean); // reset material to defaults
        driver->draw3DLine(v3f(), v3f(), video::SColor(0));
-       
+
        g_profiler->avg(prefix + "draw meshes [ms]", draw.stop(true));
        g_profiler->avg(prefix + "vertices drawn [#]", vertex_count);
        g_profiler->avg(prefix + "drawcalls [#]", drawcall_count);
+       g_profiler->avg(prefix + "material swaps [#]", material_swaps);
 }
 
 /*
        Custom update draw list for the pov of shadow light.
 */
-void ClientMap::updateDrawListShadow(const v3f &shadow_light_pos, const v3f &shadow_light_dir, float shadow_range)
+void ClientMap::updateDrawListShadow(v3f shadow_light_pos, v3f shadow_light_dir, float radius, float length)
 {
        ScopeProfiler sp(g_profiler, "CM::updateDrawListShadow()", SPT_AVG);
 
-       const v3f camera_position = shadow_light_pos;
-       const v3f camera_direction = shadow_light_dir;
-       // I "fake" fov just to avoid creating a new function to handle orthographic
-       // projection.
-       const f32 camera_fov = m_camera_fov * 1.9f;
-
-       v3s16 cam_pos_nodes = floatToInt(camera_position, BS);
+       v3s16 cam_pos_nodes = floatToInt(shadow_light_pos, BS);
        v3s16 p_blocks_min;
        v3s16 p_blocks_max;
-       getBlocksInViewRange(cam_pos_nodes, &p_blocks_min, &p_blocks_max, shadow_range);
+       getBlocksInViewRange(cam_pos_nodes, &p_blocks_min, &p_blocks_max, radius + length);
 
        std::vector<v2s16> blocks_in_range;
 
@@ -774,15 +879,6 @@ void ClientMap::updateDrawListShadow(const v3f &shadow_light_pos, const v3f &sha
        }
        m_drawlist_shadow.clear();
 
-       // We need to append the blocks from the camera POV because sometimes
-       // they are not inside the light frustum and it creates glitches.
-       // FIXME: This could be removed if we figure out why they are missing
-       // from the light frustum.
-       for (auto &i : m_drawlist) {
-               i.second->refGrab();
-               m_drawlist_shadow[i.first] = i.second;
-       }
-
        // Number of blocks currently loaded by the client
        u32 blocks_loaded = 0;
        // Number of blocks with mesh in rendering range
@@ -808,23 +904,13 @@ void ClientMap::updateDrawListShadow(const v3f &shadow_light_pos, const v3f &sha
                                continue;
                        }
 
-                       float range = shadow_range;
-
-                       float d = 0.0;
-                       if (!isBlockInSight(block->getPos(), camera_position,
-                                           camera_direction, camera_fov, range, &d))
+                       v3f block_pos = intToFloat(block->getPos() * MAP_BLOCKSIZE, BS);
+                       v3f projection = shadow_light_pos + shadow_light_dir * shadow_light_dir.dotProduct(block_pos - shadow_light_pos);
+                       if (projection.getDistanceFrom(block_pos) > radius)
                                continue;
 
                        blocks_in_range_with_mesh++;
 
-                       /*
-                               Occlusion culling
-                       */
-                       if (isBlockOccluded(block, cam_pos_nodes)) {
-                               blocks_occlusion_culled++;
-                               continue;
-                       }
-
                        // This block is in range. Reset usage timer.
                        block->resetUsageTimer();
 
@@ -841,3 +927,40 @@ void ClientMap::updateDrawListShadow(const v3f &shadow_light_pos, const v3f &sha
        g_profiler->avg("SHADOW MapBlocks drawn [#]", m_drawlist_shadow.size());
        g_profiler->avg("SHADOW MapBlocks loaded [#]", blocks_loaded);
 }
+
+void ClientMap::updateTransparentMeshBuffers()
+{
+       ScopeProfiler sp(g_profiler, "CM::updateTransparentMeshBuffers", SPT_AVG);
+       u32 sorted_blocks = 0;
+       u32 unsorted_blocks = 0;
+       f32 sorting_distance_sq = pow(m_cache_transparency_sorting_distance * BS, 2.0f);
+
+
+       // Update the order of transparent mesh buffers in each mesh
+       for (auto it = m_drawlist.begin(); it != m_drawlist.end(); it++) {
+               MapBlock* block = it->second;
+               if (!block->mesh)
+                       continue;
+               
+               if (m_needs_update_transparent_meshes || 
+                               block->mesh->getTransparentBuffers().size() == 0) {
+
+                       v3s16 block_pos = block->getPos();
+                       v3f block_pos_f = intToFloat(block_pos * MAP_BLOCKSIZE + MAP_BLOCKSIZE / 2, BS);
+                       f32 distance = m_camera_position.getDistanceFromSQ(block_pos_f);
+                       if (distance <= sorting_distance_sq) {
+                               block->mesh->updateTransparentBuffers(m_camera_position, block_pos);
+                               ++sorted_blocks;
+                       }
+                       else {
+                               block->mesh->consolidateTransparentBuffers();
+                               ++unsorted_blocks;
+                       }
+               }
+       }
+
+       g_profiler->avg("CM::Transparent Buffers - Sorted", sorted_blocks);
+       g_profiler->avg("CM::Transparent Buffers - Unsorted", unsorted_blocks);
+       m_needs_update_transparent_meshes = false;
+}
+