3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "clientmap.h"
22 #include "mapblock_mesh.h"
23 #include <IMaterialRenderer.h>
25 #include "mapsector.h"
29 #include "camera.h" // CameraModes
30 #include "util/basic_macros.h"
32 #include "client/renderingengine.h"
34 // struct MeshBufListList
35 void MeshBufListList::clear()
37 for (auto &list : lists)
41 void MeshBufListList::add(scene::IMeshBuffer *buf, v3s16 position, u8 layer)
43 // Append to the correct layer
44 std::vector<MeshBufList> &list = lists[layer];
45 const video::SMaterial &m = buf->getMaterial();
46 for (MeshBufList &l : list) {
47 // comparing a full material is quite expensive so we don't do it if
48 // not even first texture is equal
49 if (l.m.TextureLayer[0].Texture != m.TextureLayer[0].Texture)
53 l.bufs.emplace_back(position, buf);
59 l.bufs.emplace_back(position, buf);
67 RenderingEngine *rendering_engine,
68 MapDrawControl &control,
72 scene::ISceneNode(rendering_engine->get_scene_manager()->getRootSceneNode(),
73 rendering_engine->get_scene_manager(), id),
75 m_rendering_engine(rendering_engine),
80 * @Liso: Sadly C++ doesn't have introspection, so the only way we have to know
81 * the class is whith a name ;) Name property cames from ISceneNode base class.
84 m_box = aabb3f(-BS*1000000,-BS*1000000,-BS*1000000,
85 BS*1000000,BS*1000000,BS*1000000);
87 /* TODO: Add a callback function so these can be updated when a setting
88 * changes. At this point in time it doesn't matter (e.g. /set
89 * is documented to change server settings only)
91 * TODO: Local caching of settings is not optimal and should at some stage
92 * be updated to use a global settings object for getting thse values
93 * (as opposed to the this local caching). This can be addressed in
96 m_cache_trilinear_filter = g_settings->getBool("trilinear_filter");
97 m_cache_bilinear_filter = g_settings->getBool("bilinear_filter");
98 m_cache_anistropic_filter = g_settings->getBool("anisotropic_filter");
102 MapSector * ClientMap::emergeSector(v2s16 p2d)
104 // Check that it doesn't exist already
105 MapSector *sector = getSectorNoGenerate(p2d);
107 // Create it if it does not exist yet
109 sector = new MapSector(this, p2d, m_gamedef);
110 m_sectors[p2d] = sector;
116 void ClientMap::OnRegisterSceneNode()
120 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
121 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
124 ISceneNode::OnRegisterSceneNode();
126 if (!m_added_to_shadow_renderer) {
127 m_added_to_shadow_renderer = true;
128 if (auto shadows = m_rendering_engine->get_shadow_renderer())
129 shadows->addNodeToShadowList(this);
133 void ClientMap::getBlocksInViewRange(v3s16 cam_pos_nodes,
134 v3s16 *p_blocks_min, v3s16 *p_blocks_max, float range)
137 range = m_control.wanted_range;
139 v3s16 box_nodes_d = range * v3s16(1, 1, 1);
140 // Define p_nodes_min/max as v3s32 because 'cam_pos_nodes -/+ box_nodes_d'
141 // can exceed the range of v3s16 when a large view range is used near the
144 cam_pos_nodes.X - box_nodes_d.X,
145 cam_pos_nodes.Y - box_nodes_d.Y,
146 cam_pos_nodes.Z - box_nodes_d.Z);
148 cam_pos_nodes.X + box_nodes_d.X,
149 cam_pos_nodes.Y + box_nodes_d.Y,
150 cam_pos_nodes.Z + box_nodes_d.Z);
151 // Take a fair amount as we will be dropping more out later
152 // Umm... these additions are a bit strange but they are needed.
153 *p_blocks_min = v3s16(
154 p_nodes_min.X / MAP_BLOCKSIZE - 3,
155 p_nodes_min.Y / MAP_BLOCKSIZE - 3,
156 p_nodes_min.Z / MAP_BLOCKSIZE - 3);
157 *p_blocks_max = v3s16(
158 p_nodes_max.X / MAP_BLOCKSIZE + 1,
159 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
160 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
163 void ClientMap::updateDrawList()
165 ScopeProfiler sp(g_profiler, "CM::updateDrawList()", SPT_AVG);
167 for (auto &i : m_drawlist) {
168 MapBlock *block = i.second;
173 const v3f camera_position = m_camera_position;
174 const v3f camera_direction = m_camera_direction;
176 // Use a higher fov to accomodate faster camera movements.
177 // Blocks are cropped better when they are drawn.
178 const f32 camera_fov = m_camera_fov * 1.1f;
180 v3s16 cam_pos_nodes = floatToInt(camera_position, BS);
183 getBlocksInViewRange(cam_pos_nodes, &p_blocks_min, &p_blocks_max);
185 // Read the vision range, unless unlimited range is enabled.
186 float range = m_control.range_all ? 1e7 : m_control.wanted_range;
188 // Number of blocks currently loaded by the client
189 u32 blocks_loaded = 0;
190 // Number of blocks with mesh in rendering range
191 u32 blocks_in_range_with_mesh = 0;
192 // Number of blocks occlusion culled
193 u32 blocks_occlusion_culled = 0;
195 // No occlusion culling when free_move is on and camera is
197 bool occlusion_culling_enabled = true;
198 if (g_settings->getBool("free_move") && g_settings->getBool("noclip")) {
199 MapNode n = getNode(cam_pos_nodes);
200 if (n.getContent() == CONTENT_IGNORE ||
201 m_nodedef->get(n).solidness == 2)
202 occlusion_culling_enabled = false;
206 // Uncomment to debug occluded blocks in the wireframe mode
207 // TODO: Include this as a flag for an extended debugging setting
208 //if (occlusion_culling_enabled && m_control.show_wireframe)
209 // occlusion_culling_enabled = porting::getTimeS() & 1;
211 for (const auto §or_it : m_sectors) {
212 MapSector *sector = sector_it.second;
213 v2s16 sp = sector->getPos();
215 blocks_loaded += sector->size();
216 if (!m_control.range_all) {
217 if (sp.X < p_blocks_min.X || sp.X > p_blocks_max.X ||
218 sp.Y < p_blocks_min.Z || sp.Y > p_blocks_max.Z)
222 MapBlockVect sectorblocks;
223 sector->getBlocks(sectorblocks);
226 Loop through blocks in sector
229 u32 sector_blocks_drawn = 0;
231 for (MapBlock *block : sectorblocks) {
233 Compare block position to camera position, skip
234 if not seen on display
238 // Ignore if mesh doesn't exist
242 v3s16 block_coord = block->getPos();
243 v3s16 block_position = block->getPosRelative() + MAP_BLOCKSIZE / 2;
245 // First, perform a simple distance check, with a padding of one extra block.
246 if (!m_control.range_all &&
247 block_position.getDistanceFrom(cam_pos_nodes) > range + MAP_BLOCKSIZE)
248 continue; // Out of range, skip.
250 // Keep the block alive as long as it is in range.
251 block->resetUsageTimer();
252 blocks_in_range_with_mesh++;
256 if (!isBlockInSight(block_coord, camera_position,
257 camera_direction, camera_fov, range * BS, &d))
261 if ((!m_control.range_all && d > m_control.wanted_range * BS) ||
262 (occlusion_culling_enabled && isBlockOccluded(block, cam_pos_nodes))) {
263 blocks_occlusion_culled++;
269 m_drawlist[block_coord] = block;
271 sector_blocks_drawn++;
272 } // foreach sectorblocks
274 if (sector_blocks_drawn != 0)
275 m_last_drawn_sectors.insert(sp);
278 g_profiler->avg("MapBlock meshes in range [#]", blocks_in_range_with_mesh);
279 g_profiler->avg("MapBlocks occlusion culled [#]", blocks_occlusion_culled);
280 g_profiler->avg("MapBlocks drawn [#]", m_drawlist.size());
281 g_profiler->avg("MapBlocks loaded [#]", blocks_loaded);
284 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
286 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
289 if (pass == scene::ESNRP_SOLID)
290 prefix = "renderMap(SOLID): ";
292 prefix = "renderMap(TRANSPARENT): ";
295 This is called two times per frame, reset on the non-transparent one
297 if (pass == scene::ESNRP_SOLID)
298 m_last_drawn_sectors.clear();
301 Get animation parameters
303 const float animation_time = m_client->getAnimationTime();
304 const int crack = m_client->getCrackLevel();
305 const u32 daynight_ratio = m_client->getEnv().getDayNightRatio();
307 const v3f camera_position = m_camera_position;
310 Get all blocks and draw all visible ones
313 u32 vertex_count = 0;
314 u32 drawcall_count = 0;
316 // For limiting number of mesh animations per frame
317 u32 mesh_animate_count = 0;
318 //u32 mesh_animate_count_far = 0;
321 Draw the selected MapBlocks
324 MeshBufListList drawbufs;
326 for (auto &i : m_drawlist) {
327 v3s16 block_pos = i.first;
328 MapBlock *block = i.second;
330 // If the mesh of the block happened to get deleted, ignore it
334 v3f block_pos_r = intToFloat(block->getPosRelative() + MAP_BLOCKSIZE / 2, BS);
335 float d = camera_position.getDistanceFrom(block_pos_r);
336 d = MYMAX(0,d - BLOCK_MAX_RADIUS);
339 if (pass == scene::ESNRP_SOLID) {
340 MapBlockMesh *mapBlockMesh = block->mesh;
341 assert(mapBlockMesh);
342 // Pretty random but this should work somewhat nicely
343 bool faraway = d >= BS * 50;
344 if (mapBlockMesh->isAnimationForced() || !faraway ||
345 mesh_animate_count < (m_control.range_all ? 200 : 50)) {
347 bool animated = mapBlockMesh->animate(faraway, animation_time,
348 crack, daynight_ratio);
350 mesh_animate_count++;
352 mapBlockMesh->decreaseAnimationForceTimer();
357 Get the meshbuffers of the block
360 MapBlockMesh *mapBlockMesh = block->mesh;
361 assert(mapBlockMesh);
363 for (int layer = 0; layer < MAX_TILE_LAYERS; layer++) {
364 scene::IMesh *mesh = mapBlockMesh->getMesh(layer);
367 u32 c = mesh->getMeshBufferCount();
368 for (u32 i = 0; i < c; i++) {
369 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
371 video::SMaterial& material = buf->getMaterial();
372 video::IMaterialRenderer* rnd =
373 driver->getMaterialRenderer(material.MaterialType);
374 bool transparent = (rnd && rnd->isTransparent());
375 if (transparent == is_transparent_pass) {
376 if (buf->getVertexCount() == 0)
377 errorstream << "Block [" << analyze_block(block)
378 << "] contains an empty meshbuf" << std::endl;
380 material.setFlag(video::EMF_TRILINEAR_FILTER,
381 m_cache_trilinear_filter);
382 material.setFlag(video::EMF_BILINEAR_FILTER,
383 m_cache_bilinear_filter);
384 material.setFlag(video::EMF_ANISOTROPIC_FILTER,
385 m_cache_anistropic_filter);
386 material.setFlag(video::EMF_WIREFRAME,
387 m_control.show_wireframe);
389 drawbufs.add(buf, block_pos, layer);
396 TimeTaker draw("Drawing mesh buffers");
398 core::matrix4 m; // Model matrix
399 v3f offset = intToFloat(m_camera_offset, BS);
401 // Render all layers in order
402 for (auto &lists : drawbufs.lists) {
403 for (MeshBufList &list : lists) {
404 // Check and abort if the machine is swapping a lot
405 if (draw.getTimerTime() > 2000) {
406 infostream << "ClientMap::renderMap(): Rendering took >2s, " <<
407 "returning." << std::endl;
411 // pass the shadow map texture to the buffer texture
412 ShadowRenderer *shadow = m_rendering_engine->get_shadow_renderer();
413 if (shadow && shadow->is_active()) {
414 auto &layer = list.m.TextureLayer[3];
415 layer.Texture = shadow->get_texture();
416 layer.TextureWrapU = video::E_TEXTURE_CLAMP::ETC_CLAMP_TO_EDGE;
417 layer.TextureWrapV = video::E_TEXTURE_CLAMP::ETC_CLAMP_TO_EDGE;
418 layer.TrilinearFilter = true;
421 driver->setMaterial(list.m);
423 drawcall_count += list.bufs.size();
424 for (auto &pair : list.bufs) {
425 scene::IMeshBuffer *buf = pair.second;
427 v3f block_wpos = intToFloat(pair.first * MAP_BLOCKSIZE, BS);
428 m.setTranslation(block_wpos - offset);
430 driver->setTransform(video::ETS_WORLD, m);
431 driver->drawMeshBuffer(buf);
432 vertex_count += buf->getVertexCount();
436 g_profiler->avg(prefix + "draw meshes [ms]", draw.stop(true));
438 // Log only on solid pass because values are the same
439 if (pass == scene::ESNRP_SOLID) {
440 g_profiler->avg("renderMap(): animated meshes [#]", mesh_animate_count);
443 g_profiler->avg(prefix + "vertices drawn [#]", vertex_count);
444 g_profiler->avg(prefix + "drawcalls [#]", drawcall_count);
447 static bool getVisibleBrightness(Map *map, const v3f &p0, v3f dir, float step,
448 float step_multiplier, float start_distance, float end_distance,
449 const NodeDefManager *ndef, u32 daylight_factor, float sunlight_min_d,
450 int *result, bool *sunlight_seen)
452 int brightness_sum = 0;
453 int brightness_count = 0;
454 float distance = start_distance;
457 pf += dir * distance;
459 bool nonlight_seen = false;
460 bool allow_allowing_non_sunlight_propagates = false;
461 bool allow_non_sunlight_propagates = false;
462 // Check content nearly at camera position
464 v3s16 p = floatToInt(p0 /*+ dir * 3*BS*/, BS);
465 MapNode n = map->getNode(p);
466 if(ndef->get(n).param_type == CPT_LIGHT &&
467 !ndef->get(n).sunlight_propagates)
468 allow_allowing_non_sunlight_propagates = true;
470 // If would start at CONTENT_IGNORE, start closer
472 v3s16 p = floatToInt(pf, BS);
473 MapNode n = map->getNode(p);
474 if(n.getContent() == CONTENT_IGNORE){
476 pf = p0 + dir * 2*newd;
481 for (int i=0; distance < end_distance; i++) {
484 step *= step_multiplier;
486 v3s16 p = floatToInt(pf, BS);
487 MapNode n = map->getNode(p);
488 if (allow_allowing_non_sunlight_propagates && i == 0 &&
489 ndef->get(n).param_type == CPT_LIGHT &&
490 !ndef->get(n).sunlight_propagates) {
491 allow_non_sunlight_propagates = true;
494 if (ndef->get(n).param_type != CPT_LIGHT ||
495 (!ndef->get(n).sunlight_propagates &&
496 !allow_non_sunlight_propagates)){
497 nonlight_seen = true;
504 if (distance >= sunlight_min_d && !*sunlight_seen && !nonlight_seen)
505 if (n.getLight(LIGHTBANK_DAY, ndef) == LIGHT_SUN)
506 *sunlight_seen = true;
508 brightness_sum += decode_light(n.getLightBlend(daylight_factor, ndef));
512 if(brightness_count == 0)
514 *result = brightness_sum / brightness_count;
515 /*std::cerr<<"Sampled "<<brightness_count<<" points; result="
516 <<(*result)<<std::endl;*/
520 int ClientMap::getBackgroundBrightness(float max_d, u32 daylight_factor,
521 int oldvalue, bool *sunlight_seen_result)
523 ScopeProfiler sp(g_profiler, "CM::getBackgroundBrightness", SPT_AVG);
524 static v3f z_directions[50] = {
527 static f32 z_offsets[50] = {
531 if (z_directions[0].X < -99) {
532 for (u32 i = 0; i < ARRLEN(z_directions); i++) {
533 // Assumes FOV of 72 and 16/9 aspect ratio
534 z_directions[i] = v3f(
535 0.02 * myrand_range(-100, 100),
537 0.01 * myrand_range(-100, 100)
539 z_offsets[i] = 0.01 * myrand_range(0,100);
543 int sunlight_seen_count = 0;
544 float sunlight_min_d = max_d*0.8;
545 if(sunlight_min_d > 35*BS)
546 sunlight_min_d = 35*BS;
547 std::vector<int> values;
548 values.reserve(ARRLEN(z_directions));
549 for (u32 i = 0; i < ARRLEN(z_directions); i++) {
550 v3f z_dir = z_directions[i];
551 core::CMatrix4<f32> a;
552 a.buildRotateFromTo(v3f(0,1,0), z_dir);
553 v3f dir = m_camera_direction;
558 step = max_d / 35 * 1.5;
559 float off = step * z_offsets[i];
560 bool sunlight_seen_now = false;
561 bool ok = getVisibleBrightness(this, m_camera_position, dir,
562 step, 1.0, max_d*0.6+off, max_d, m_nodedef, daylight_factor,
564 &br, &sunlight_seen_now);
565 if(sunlight_seen_now)
566 sunlight_seen_count++;
569 values.push_back(br);
570 // Don't try too much if being in the sun is clear
571 if(sunlight_seen_count >= 20)
574 int brightness_sum = 0;
575 int brightness_count = 0;
576 std::sort(values.begin(), values.end());
577 u32 num_values_to_use = values.size();
578 if(num_values_to_use >= 10)
579 num_values_to_use -= num_values_to_use/2;
580 else if(num_values_to_use >= 7)
581 num_values_to_use -= num_values_to_use/3;
582 u32 first_value_i = (values.size() - num_values_to_use) / 2;
584 for (u32 i=first_value_i; i < first_value_i + num_values_to_use; i++) {
585 brightness_sum += values[i];
590 if(brightness_count == 0){
591 MapNode n = getNode(floatToInt(m_camera_position, BS));
592 if(m_nodedef->get(n).param_type == CPT_LIGHT){
593 ret = decode_light(n.getLightBlend(daylight_factor, m_nodedef));
598 ret = brightness_sum / brightness_count;
601 *sunlight_seen_result = (sunlight_seen_count > 0);
605 void ClientMap::renderPostFx(CameraMode cam_mode)
607 // Sadly ISceneManager has no "post effects" render pass, in that case we
608 // could just register for that and handle it in renderMap().
610 MapNode n = getNode(floatToInt(m_camera_position, BS));
612 // - If the player is in a solid node, make everything black.
613 // - If the player is in liquid, draw a semi-transparent overlay.
614 // - Do not if player is in third person mode
615 const ContentFeatures& features = m_nodedef->get(n);
616 video::SColor post_effect_color = features.post_effect_color;
617 if(features.solidness == 2 && !(g_settings->getBool("noclip") &&
618 m_client->checkLocalPrivilege("noclip")) &&
619 cam_mode == CAMERA_MODE_FIRST)
621 post_effect_color = video::SColor(255, 0, 0, 0);
623 if (post_effect_color.getAlpha() != 0)
625 // Draw a full-screen rectangle
626 video::IVideoDriver* driver = SceneManager->getVideoDriver();
627 v2u32 ss = driver->getScreenSize();
628 core::rect<s32> rect(0,0, ss.X, ss.Y);
629 driver->draw2DRectangle(post_effect_color, rect);
633 void ClientMap::PrintInfo(std::ostream &out)
638 void ClientMap::renderMapShadows(video::IVideoDriver *driver,
639 const video::SMaterial &material, s32 pass)
641 bool is_transparent_pass = pass != scene::ESNRP_SOLID;
643 if (is_transparent_pass)
644 prefix = "renderMap(SHADOW TRANS): ";
646 prefix = "renderMap(SHADOW SOLID): ";
648 u32 drawcall_count = 0;
649 u32 vertex_count = 0;
651 MeshBufListList drawbufs;
653 for (auto &i : m_drawlist_shadow) {
654 v3s16 block_pos = i.first;
655 MapBlock *block = i.second;
657 // If the mesh of the block happened to get deleted, ignore it
662 Get the meshbuffers of the block
665 MapBlockMesh *mapBlockMesh = block->mesh;
666 assert(mapBlockMesh);
668 for (int layer = 0; layer < MAX_TILE_LAYERS; layer++) {
669 scene::IMesh *mesh = mapBlockMesh->getMesh(layer);
672 u32 c = mesh->getMeshBufferCount();
673 for (u32 i = 0; i < c; i++) {
674 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
676 video::SMaterial &mat = buf->getMaterial();
677 auto rnd = driver->getMaterialRenderer(mat.MaterialType);
678 bool transparent = rnd && rnd->isTransparent();
679 if (transparent == is_transparent_pass)
680 drawbufs.add(buf, block_pos, layer);
686 TimeTaker draw("Drawing shadow mesh buffers");
688 core::matrix4 m; // Model matrix
689 v3f offset = intToFloat(m_camera_offset, BS);
691 // Render all layers in order
692 for (auto &lists : drawbufs.lists) {
693 for (MeshBufList &list : lists) {
694 // Check and abort if the machine is swapping a lot
695 if (draw.getTimerTime() > 1000) {
696 infostream << "ClientMap::renderMapShadows(): Rendering "
697 "took >1s, returning." << std::endl;
700 for (auto &pair : list.bufs) {
701 scene::IMeshBuffer *buf = pair.second;
703 // override some material properties
704 video::SMaterial local_material = buf->getMaterial();
705 local_material.MaterialType = material.MaterialType;
706 local_material.BackfaceCulling = material.BackfaceCulling;
707 local_material.FrontfaceCulling = material.FrontfaceCulling;
708 local_material.Lighting = false;
709 driver->setMaterial(local_material);
711 v3f block_wpos = intToFloat(pair.first * MAP_BLOCKSIZE, BS);
712 m.setTranslation(block_wpos - offset);
714 driver->setTransform(video::ETS_WORLD, m);
715 driver->drawMeshBuffer(buf);
716 vertex_count += buf->getVertexCount();
719 drawcall_count += list.bufs.size();
723 g_profiler->avg(prefix + "draw meshes [ms]", draw.stop(true));
724 g_profiler->avg(prefix + "vertices drawn [#]", vertex_count);
725 g_profiler->avg(prefix + "drawcalls [#]", drawcall_count);
729 Custom update draw list for the pov of shadow light.
731 void ClientMap::updateDrawListShadow(const v3f &shadow_light_pos, const v3f &shadow_light_dir, float shadow_range)
733 ScopeProfiler sp(g_profiler, "CM::updateDrawListShadow()", SPT_AVG);
735 const v3f camera_position = shadow_light_pos;
736 const v3f camera_direction = shadow_light_dir;
737 // I "fake" fov just to avoid creating a new function to handle orthographic
739 const f32 camera_fov = m_camera_fov * 1.9f;
741 v3s16 cam_pos_nodes = floatToInt(camera_position, BS);
744 getBlocksInViewRange(cam_pos_nodes, &p_blocks_min, &p_blocks_max, shadow_range);
746 std::vector<v2s16> blocks_in_range;
748 for (auto &i : m_drawlist_shadow) {
749 MapBlock *block = i.second;
752 m_drawlist_shadow.clear();
754 // We need to append the blocks from the camera POV because sometimes
755 // they are not inside the light frustum and it creates glitches.
756 // FIXME: This could be removed if we figure out why they are missing
757 // from the light frustum.
758 for (auto &i : m_drawlist) {
760 m_drawlist_shadow[i.first] = i.second;
763 // Number of blocks currently loaded by the client
764 u32 blocks_loaded = 0;
765 // Number of blocks with mesh in rendering range
766 u32 blocks_in_range_with_mesh = 0;
767 // Number of blocks occlusion culled
768 u32 blocks_occlusion_culled = 0;
770 for (auto §or_it : m_sectors) {
771 MapSector *sector = sector_it.second;
774 blocks_loaded += sector->size();
776 MapBlockVect sectorblocks;
777 sector->getBlocks(sectorblocks);
780 Loop through blocks in sector
782 for (MapBlock *block : sectorblocks) {
784 // Ignore if mesh doesn't exist
788 float range = shadow_range;
791 if (!isBlockInSight(block->getPos(), camera_position,
792 camera_direction, camera_fov, range, &d))
795 blocks_in_range_with_mesh++;
800 if (isBlockOccluded(block, cam_pos_nodes)) {
801 blocks_occlusion_culled++;
805 // This block is in range. Reset usage timer.
806 block->resetUsageTimer();
809 if (m_drawlist_shadow.find(block->getPos()) == m_drawlist_shadow.end()) {
811 m_drawlist_shadow[block->getPos()] = block;
816 g_profiler->avg("SHADOW MapBlock meshes in range [#]", blocks_in_range_with_mesh);
817 g_profiler->avg("SHADOW MapBlocks occlusion culled [#]", blocks_occlusion_culled);
818 g_profiler->avg("SHADOW MapBlocks drawn [#]", m_drawlist_shadow.size());
819 g_profiler->avg("SHADOW MapBlocks loaded [#]", blocks_loaded);