X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=src%2Fclient%2Fclientmap.cpp;h=51f0f6896774dc10a64cb622c0b5499f492a5586;hb=21df26984da91143c15587f5a03c98d68c3adc4e;hp=4a4784f91b13e575c1b6aef9299fcc0d0cec6113;hpb=b09fc5de5cdb021f43ad32b7e3f50dc75c0bc622;p=dragonfireclient.git diff --git a/src/client/clientmap.cpp b/src/client/clientmap.cpp index 4a4784f91..51f0f6896 100644 --- a/src/client/clientmap.cpp +++ b/src/client/clientmap.cpp @@ -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(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 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 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 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; +} +