From 5f1cd555cd9d1c64426e173b30b5b792d211c835 Mon Sep 17 00:00:00 2001 From: Quentin Bazin Date: Wed, 28 Nov 2018 20:01:49 +0100 Subject: Move client-specific files to 'src/client' (#7902) Update Android.mk Remove 'src/client' from include_directories --- src/client/clientmap.cpp | 671 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 671 insertions(+) create mode 100644 src/client/clientmap.cpp (limited to 'src/client/clientmap.cpp') diff --git a/src/client/clientmap.cpp b/src/client/clientmap.cpp new file mode 100644 index 000000000..969c55539 --- /dev/null +++ b/src/client/clientmap.cpp @@ -0,0 +1,671 @@ +/* +Minetest +Copyright (C) 2010-2013 celeron55, Perttu Ahola + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "clientmap.h" +#include "client.h" +#include "mapblock_mesh.h" +#include +#include +#include "mapsector.h" +#include "mapblock.h" +#include "profiler.h" +#include "settings.h" +#include "camera.h" // CameraModes +#include "util/basic_macros.h" +#include +#include "client/renderingengine.h" + +ClientMap::ClientMap( + Client *client, + MapDrawControl &control, + s32 id +): + Map(dout_client, client), + scene::ISceneNode(RenderingEngine::get_scene_manager()->getRootSceneNode(), + RenderingEngine::get_scene_manager(), id), + m_client(client), + m_control(control) +{ + m_box = aabb3f(-BS*1000000,-BS*1000000,-BS*1000000, + BS*1000000,BS*1000000,BS*1000000); + + /* TODO: Add a callback function so these can be updated when a setting + * changes. At this point in time it doesn't matter (e.g. /set + * is documented to change server settings only) + * + * TODO: Local caching of settings is not optimal and should at some stage + * be updated to use a global settings object for getting thse values + * (as opposed to the this local caching). This can be addressed in + * a later release. + */ + 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"); + +} + +MapSector * ClientMap::emergeSector(v2s16 p2d) +{ + // Check that it doesn't exist already + try { + return getSectorNoGenerate(p2d); + } catch(InvalidPositionException &e) { + } + + // Create a sector + MapSector *sector = new MapSector(this, p2d, m_gamedef); + m_sectors[p2d] = sector; + + return sector; +} + +void ClientMap::OnRegisterSceneNode() +{ + if(IsVisible) + { + SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID); + SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT); + } + + ISceneNode::OnRegisterSceneNode(); +} + +void ClientMap::getBlocksInViewRange(v3s16 cam_pos_nodes, + v3s16 *p_blocks_min, v3s16 *p_blocks_max) +{ + v3s16 box_nodes_d = m_control.wanted_range * v3s16(1, 1, 1); + // Define p_nodes_min/max as v3s32 because 'cam_pos_nodes -/+ box_nodes_d' + // can exceed the range of v3s16 when a large view range is used near the + // world edges. + v3s32 p_nodes_min( + cam_pos_nodes.X - box_nodes_d.X, + cam_pos_nodes.Y - box_nodes_d.Y, + cam_pos_nodes.Z - box_nodes_d.Z); + v3s32 p_nodes_max( + cam_pos_nodes.X + box_nodes_d.X, + cam_pos_nodes.Y + box_nodes_d.Y, + cam_pos_nodes.Z + box_nodes_d.Z); + // Take a fair amount as we will be dropping more out later + // Umm... these additions are a bit strange but they are needed. + *p_blocks_min = v3s16( + p_nodes_min.X / MAP_BLOCKSIZE - 3, + p_nodes_min.Y / MAP_BLOCKSIZE - 3, + p_nodes_min.Z / MAP_BLOCKSIZE - 3); + *p_blocks_max = v3s16( + p_nodes_max.X / MAP_BLOCKSIZE + 1, + p_nodes_max.Y / MAP_BLOCKSIZE + 1, + p_nodes_max.Z / MAP_BLOCKSIZE + 1); +} + +void ClientMap::updateDrawList() +{ + ScopeProfiler sp(g_profiler, "CM::updateDrawList()", SPT_AVG); + g_profiler->add("CM::updateDrawList() count", 1); + + for (auto &i : m_drawlist) { + MapBlock *block = i.second; + block->refDrop(); + } + m_drawlist.clear(); + + v3f camera_position = m_camera_position; + v3f camera_direction = m_camera_direction; + f32 camera_fov = m_camera_fov; + + // Use a higher fov to accomodate faster camera movements. + // Blocks are cropped better when they are drawn. + // Or maybe they aren't? Well whatever. + camera_fov *= 1.2; + + 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); + + // Number of blocks in rendering range + u32 blocks_in_range = 0; + // Number of blocks occlusion culled + u32 blocks_occlusion_culled = 0; + // Number of blocks in rendering range but don't have a mesh + u32 blocks_in_range_without_mesh = 0; + // Blocks that had mesh that would have been drawn according to + // rendering range (if max blocks limit didn't kick in) + u32 blocks_would_have_drawn = 0; + // Blocks that were drawn and had a mesh + u32 blocks_drawn = 0; + // Blocks which had a corresponding meshbuffer for this pass + //u32 blocks_had_pass_meshbuf = 0; + // Blocks from which stuff was actually drawn + //u32 blocks_without_stuff = 0; + // Distance to farthest drawn block + float farthest_drawn = 0; + + // No occlusion culling when free_move is on and camera is + // inside ground + bool occlusion_culling_enabled = true; + if (g_settings->getBool("free_move")) { + MapNode n = getNodeNoEx(cam_pos_nodes); + if (n.getContent() == CONTENT_IGNORE || + m_nodedef->get(n).solidness == 2) + occlusion_culling_enabled = false; + } + + for (const auto §or_it : m_sectors) { + MapSector *sector = sector_it.second; + v2s16 sp = sector->getPos(); + + if (!m_control.range_all) { + if (sp.X < p_blocks_min.X || sp.X > p_blocks_max.X || + sp.Y < p_blocks_min.Z || sp.Y > p_blocks_max.Z) + continue; + } + + MapBlockVect sectorblocks; + sector->getBlocks(sectorblocks); + + /* + Loop through blocks in sector + */ + + u32 sector_blocks_drawn = 0; + + for (auto block : sectorblocks) { + /* + Compare block position to camera position, skip + if not seen on display + */ + + if (block->mesh) + block->mesh->updateCameraOffset(m_camera_offset); + + float range = 100000 * BS; + if (!m_control.range_all) + range = m_control.wanted_range * BS; + + float d = 0.0; + if (!isBlockInSight(block->getPos(), camera_position, + camera_direction, camera_fov, range, &d)) + continue; + + blocks_in_range++; + + /* + Ignore if mesh doesn't exist + */ + if (!block->mesh) { + blocks_in_range_without_mesh++; + continue; + } + + /* + Occlusion culling + */ + if (occlusion_culling_enabled && isBlockOccluded(block, cam_pos_nodes)) { + blocks_occlusion_culled++; + continue; + } + + // This block is in range. Reset usage timer. + block->resetUsageTimer(); + + // Limit block count in case of a sudden increase + blocks_would_have_drawn++; + if (blocks_drawn >= m_control.wanted_max_blocks && + !m_control.range_all && + d > m_control.wanted_range * BS) + continue; + + // Add to set + block->refGrab(); + m_drawlist[block->getPos()] = block; + + sector_blocks_drawn++; + blocks_drawn++; + if (d / BS > farthest_drawn) + farthest_drawn = d / BS; + + } // foreach sectorblocks + + if (sector_blocks_drawn != 0) + m_last_drawn_sectors.insert(sp); + } + + g_profiler->avg("CM: blocks in range", blocks_in_range); + g_profiler->avg("CM: blocks occlusion culled", blocks_occlusion_culled); + if (blocks_in_range != 0) + g_profiler->avg("CM: blocks in range without mesh (frac)", + (float)blocks_in_range_without_mesh / blocks_in_range); + g_profiler->avg("CM: blocks drawn", blocks_drawn); + g_profiler->avg("CM: farthest drawn", farthest_drawn); + g_profiler->avg("CM: wanted max blocks", m_control.wanted_max_blocks); +} + +struct MeshBufList +{ + video::SMaterial m; + std::vector bufs; +}; + +struct MeshBufListList +{ + /*! + * Stores the mesh buffers of the world. + * The array index is the material's layer. + * The vector part groups vertices by material. + */ + std::vector lists[MAX_TILE_LAYERS]; + + void clear() + { + for (auto &list : lists) + list.clear(); + } + + void add(scene::IMeshBuffer *buf, u8 layer) + { + // Append to the correct layer + std::vector &list = lists[layer]; + const video::SMaterial &m = buf->getMaterial(); + for (MeshBufList &l : list) { + // comparing a full material is quite expensive so we don't do it if + // not even first texture is equal + if (l.m.TextureLayer[0].Texture != m.TextureLayer[0].Texture) + continue; + + if (l.m == m) { + l.bufs.push_back(buf); + return; + } + } + MeshBufList l; + l.m = m; + l.bufs.push_back(buf); + list.push_back(l); + } +}; + +void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) +{ + bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT; + + std::string prefix; + if (pass == scene::ESNRP_SOLID) + prefix = "CM: solid: "; + else + prefix = "CM: transparent: "; + + /* + This is called two times per frame, reset on the non-transparent one + */ + if (pass == scene::ESNRP_SOLID) + m_last_drawn_sectors.clear(); + + /* + Get time for measuring timeout. + + Measuring time is very useful for long delays when the + machine is swapping a lot. + */ + std::time_t time1 = time(0); + + /* + Get animation parameters + */ + float animation_time = m_client->getAnimationTime(); + int crack = m_client->getCrackLevel(); + u32 daynight_ratio = m_client->getEnv().getDayNightRatio(); + + v3f camera_position = m_camera_position; + v3f camera_direction = m_camera_direction; + f32 camera_fov = m_camera_fov; + + /* + Get all blocks and draw all visible ones + */ + + u32 vertex_count = 0; + u32 meshbuffer_count = 0; + + // For limiting number of mesh animations per frame + u32 mesh_animate_count = 0; + u32 mesh_animate_count_far = 0; + + // Blocks that were drawn and had a mesh + u32 blocks_drawn = 0; + // Blocks which had a corresponding meshbuffer for this pass + u32 blocks_had_pass_meshbuf = 0; + // Blocks from which stuff was actually drawn + u32 blocks_without_stuff = 0; + + /* + Draw the selected MapBlocks + */ + + { + ScopeProfiler sp(g_profiler, prefix + "drawing blocks", SPT_AVG); + + MeshBufListList drawbufs; + + for (auto &i : m_drawlist) { + 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, + camera_direction, camera_fov, 100000 * BS, &d)) + continue; + + // Mesh animation + if (pass == scene::ESNRP_SOLID) { + //MutexAutoLock lock(block->mesh_mutex); + MapBlockMesh *mapBlockMesh = block->mesh; + assert(mapBlockMesh); + // Pretty random but this should work somewhat nicely + bool faraway = d >= BS * 50; + //bool faraway = d >= m_control.wanted_range * BS; + if (mapBlockMesh->isAnimationForced() || !faraway || + mesh_animate_count_far < (m_control.range_all ? 200 : 50)) { + bool animated = mapBlockMesh->animate(faraway, animation_time, + crack, daynight_ratio); + if (animated) + mesh_animate_count++; + if (animated && faraway) + mesh_animate_count_far++; + } else { + mapBlockMesh->decreaseAnimationForceTimer(); + } + } + + /* + Get the meshbuffers of the block + */ + { + //MutexAutoLock lock(block->mesh_mutex); + + MapBlockMesh *mapBlockMesh = block->mesh; + assert(mapBlockMesh); + + for (int layer = 0; layer < MAX_TILE_LAYERS; layer++) { + scene::IMesh *mesh = mapBlockMesh->getMesh(layer); + assert(mesh); + + u32 c = mesh->getMeshBufferCount(); + for (u32 i = 0; i < c; i++) { + scene::IMeshBuffer *buf = mesh->getMeshBuffer(i); + + video::SMaterial& material = buf->getMaterial(); + video::IMaterialRenderer* rnd = + driver->getMaterialRenderer(material.MaterialType); + bool transparent = (rnd && rnd->isTransparent()); + if (transparent == is_transparent_pass) { + 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, layer); + } + } + } + } + } + + // Render all layers in order + for (auto &lists : drawbufs.lists) { + int timecheck_counter = 0; + for (MeshBufList &list : lists) { + timecheck_counter++; + if (timecheck_counter > 50) { + timecheck_counter = 0; + std::time_t time2 = time(0); + if (time2 > time1 + 4) { + infostream << "ClientMap::renderMap(): " + "Rendering takes ages, returning." + << std::endl; + return; + } + } + + driver->setMaterial(list.m); + + for (scene::IMeshBuffer *buf : list.bufs) { + driver->drawMeshBuffer(buf); + vertex_count += buf->getVertexCount(); + meshbuffer_count++; + } + } + } + } // ScopeProfiler + + // Log only on solid pass because values are the same + if (pass == scene::ESNRP_SOLID) { + g_profiler->avg("CM: animated meshes", mesh_animate_count); + g_profiler->avg("CM: animated meshes (far)", mesh_animate_count_far); + } + + g_profiler->avg(prefix + "vertices drawn", vertex_count); + if (blocks_had_pass_meshbuf != 0) + g_profiler->avg(prefix + "meshbuffers per block", + (float)meshbuffer_count / (float)blocks_had_pass_meshbuf); + if (blocks_drawn != 0) + g_profiler->avg(prefix + "empty blocks (frac)", + (float)blocks_without_stuff / blocks_drawn); +} + +static bool getVisibleBrightness(Map *map, const v3f &p0, v3f dir, float step, + float step_multiplier, float start_distance, float end_distance, + const NodeDefManager *ndef, u32 daylight_factor, float sunlight_min_d, + int *result, bool *sunlight_seen) +{ + int brightness_sum = 0; + int brightness_count = 0; + float distance = start_distance; + dir.normalize(); + v3f pf = p0; + pf += dir * distance; + int noncount = 0; + bool nonlight_seen = false; + bool allow_allowing_non_sunlight_propagates = false; + bool allow_non_sunlight_propagates = false; + // Check content nearly at camera position + { + v3s16 p = floatToInt(p0 /*+ dir * 3*BS*/, BS); + MapNode n = map->getNodeNoEx(p); + if(ndef->get(n).param_type == CPT_LIGHT && + !ndef->get(n).sunlight_propagates) + allow_allowing_non_sunlight_propagates = true; + } + // If would start at CONTENT_IGNORE, start closer + { + v3s16 p = floatToInt(pf, BS); + MapNode n = map->getNodeNoEx(p); + if(n.getContent() == CONTENT_IGNORE){ + float newd = 2*BS; + pf = p0 + dir * 2*newd; + distance = newd; + sunlight_min_d = 0; + } + } + for (int i=0; distance < end_distance; i++) { + pf += dir * step; + distance += step; + step *= step_multiplier; + + v3s16 p = floatToInt(pf, BS); + MapNode n = map->getNodeNoEx(p); + if (allow_allowing_non_sunlight_propagates && i == 0 && + ndef->get(n).param_type == CPT_LIGHT && + !ndef->get(n).sunlight_propagates) { + allow_non_sunlight_propagates = true; + } + + if (ndef->get(n).param_type != CPT_LIGHT || + (!ndef->get(n).sunlight_propagates && + !allow_non_sunlight_propagates)){ + nonlight_seen = true; + noncount++; + if(noncount >= 4) + break; + continue; + } + + if (distance >= sunlight_min_d && !*sunlight_seen && !nonlight_seen) + if (n.getLight(LIGHTBANK_DAY, ndef) == LIGHT_SUN) + *sunlight_seen = true; + noncount = 0; + brightness_sum += decode_light(n.getLightBlend(daylight_factor, ndef)); + brightness_count++; + } + *result = 0; + if(brightness_count == 0) + return false; + *result = brightness_sum / brightness_count; + /*std::cerr<<"Sampled "<get(n); + video::SColor post_effect_color = features.post_effect_color; + if(features.solidness == 2 && !(g_settings->getBool("noclip") && + m_client->checkLocalPrivilege("noclip")) && + cam_mode == CAMERA_MODE_FIRST) + { + post_effect_color = video::SColor(255, 0, 0, 0); + } + if (post_effect_color.getAlpha() != 0) + { + // Draw a full-screen rectangle + video::IVideoDriver* driver = SceneManager->getVideoDriver(); + v2u32 ss = driver->getScreenSize(); + core::rect rect(0,0, ss.X, ss.Y); + driver->draw2DRectangle(post_effect_color, rect); + } +} + +void ClientMap::PrintInfo(std::ostream &out) +{ + out<<"ClientMap: "; +} + + -- cgit v1.2.3