diff --git a/src/client/clientmap.cpp b/src/client/clientmap.cpp index f2db94296..3e4ab2e94 100644 --- a/src/client/clientmap.cpp +++ b/src/client/clientmap.cpp @@ -151,6 +151,11 @@ void ClientMap::updateDrawList() occlusion_culling_enabled = false; } + // Uncomment to debug occluded blocks in the wireframe mode + // TODO: Include this as a flag for an extended debugging setting + //if (occlusion_culling_enabled && m_control.show_wireframe) + // occlusion_culling_enabled = porting::getTimeS() & 1; + for (const auto §or_it : m_sectors) { MapSector *sector = sector_it.second; v2s16 sp = sector->getPos(); diff --git a/src/map.cpp b/src/map.cpp index 3b57a9493..6b027d34b 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -1032,21 +1032,46 @@ void Map::removeNodeTimer(v3s16 p) block->m_node_timers.remove(p_rel); } -bool Map::isOccluded(v3s16 pos_origin, v3s16 pos_blockcenter, float step, - float stepfac, float offset, float end_offset, u32 needed_count) +bool Map::isOccluded(const v3s16 &pos_camera, const v3s16 &pos_target, + const core::aabbox3d &block_bounds, float step, float stepfac, float offset, + u32 needed_count) { - float distance = BS * pos_origin.getDistanceFrom(pos_blockcenter); - v3f direction = intToFloat(pos_blockcenter - pos_origin, BS); - direction.normalize(); - v3f pos_origin_f = intToFloat(pos_origin, BS); + // Worst-case safety distance to keep to the target position + // Anything smaller than the mapblock diagonal could result in in self-occlusion + // Diagonal = sqrt(1*1 + 1*1 + 1*1) + const static float BLOCK_DIAGONAL = BS * MAP_BLOCKSIZE * 1.732f; + v3f direction = intToFloat(pos_target - pos_camera, BS); + float distance = direction.getLength(); + + // Disable occlusion culling for near mapblocks in underground + if (distance < BLOCK_DIAGONAL) + return false; + + // Normalize direction vector + if (distance > 0.0f) + direction /= distance; + + v3f pos_origin_f = intToFloat(pos_camera, BS); u32 count = 0; - for (; offset < distance + end_offset; offset += step){ - v3f pf = pos_origin_f + direction * offset; - v3s16 pos_node = floatToInt(pf, BS); - MapNode node = getNode(pos_node); - if (m_nodedef->get(node).drawtype == NDT_NORMAL) { - // not transparent, see ContentFeature::updateTextures + bool is_valid_position; + + for (; offset < distance; offset += step) { + v3f pos_node_f = pos_origin_f + direction * offset; + v3s16 pos_node = floatToInt(pos_node_f, BS); + + if (offset > distance - BLOCK_DIAGONAL) { + // Do accurate position checks: + // Check whether the node is inside the current mapblock + if (block_bounds.isPointInside(pos_node)) + return false; + } + + MapNode node = getNode(pos_node, &is_valid_position); + + if (is_valid_position && + !m_nodedef->get(node).light_propagates) { + // Cannot see through light-blocking nodes --> occluded count++; if (count >= needed_count) return true; @@ -1058,8 +1083,11 @@ bool Map::isOccluded(v3s16 pos_origin, v3s16 pos_blockcenter, float step, bool Map::isBlockOccluded(MapBlock *block, v3s16 cam_pos_nodes) { + // Check occlusion for center and all 8 corners of the mapblock + // Overshoot a little for less flickering static const s16 bs2 = MAP_BLOCKSIZE / 2 + 1; - static const v3s16 dir8[8] = { + static const v3s16 dir9[9] = { + v3s16( 0, 0, 0), v3s16( 1, 1, 1) * bs2, v3s16( 1, 1, -1) * bs2, v3s16( 1, -1, 1) * bs2, @@ -1070,34 +1098,26 @@ bool Map::isBlockOccluded(MapBlock *block, v3s16 cam_pos_nodes) v3s16(-1, -1, -1) * bs2, }; - v3s16 pos_blockcenter = block->getPos() * MAP_BLOCKSIZE; - pos_blockcenter += v3s16(MAP_BLOCKSIZE) / 2; + // Minimal and maximal positions in the mapblock + core::aabbox3d block_bounds = block->getBox(); - float step = BS * 1; + v3s16 pos_blockcenter = block_bounds.MinEdge + (MAP_BLOCKSIZE / 2); + + // Starting step size, value between 1m and sqrt(3)m + float step = BS * 1.2f; // Multiply step by each iteraction by 'stepfac' to reduce checks in distance - float stepfac = 1.1; + float stepfac = 1.05f; - float start_offset = BS * 1; - // The occlusion search of 'isOccluded()' must stop short of the target - // point by distance 'end_offset' (end offset) to not enter the target mapblock. - // For the 8 mapblock corners 'end_offset' must therefore be the maximum diagonal - // of a mapblock, because we must consider all view angles. - // sqrt(1^2 + 1^2 + 1^2) = 1.732 - float end_offset = -BS * MAP_BLOCKSIZE * 1.7321; + float start_offset = BS * 1.0f; // to reduce the likelihood of falsely occluded blocks // require at least two solid blocks // this is a HACK, we should think of a more precise algorithm u32 needed_count = 2; - // For the central point of the mapblock 'end_offset' can be halved - if (!isOccluded(cam_pos_nodes, pos_blockcenter, - step, stepfac, start_offset, end_offset / 2.0f, needed_count)) - return false; - - for (const v3s16 &dir : dir8) { - if (!isOccluded(cam_pos_nodes, pos_blockcenter + dir, - step, stepfac, start_offset, end_offset, needed_count)) + for (const v3s16 &dir : dir9) { + if (!isOccluded(cam_pos_nodes, pos_blockcenter + dir, block_bounds, + step, stepfac, start_offset, needed_count)) return false; } return true; diff --git a/src/map.h b/src/map.h index d26da37a6..232a25ac7 100644 --- a/src/map.h +++ b/src/map.h @@ -310,8 +310,9 @@ protected: // This stores the properties of the nodes on the map. const NodeDefManager *m_nodedef; - bool isOccluded(v3s16 pos_origin, v3s16 pos_blockcenter, float step, - float stepfac, float start_offset, float end_offset, u32 needed_count); + bool isOccluded(const v3s16 &pos_camera, const v3s16 &pos_target, + const core::aabbox3d &block_bounds, float step, float stepfac, + float offset, u32 needed_count); private: f32 m_transforming_liquid_loop_count_multiplier = 1.0f;