Occlusion: Check for light_propagates and do mapblock bounds checks
authorSmallJoker <mk939@ymail.com>
Sat, 17 Aug 2019 13:42:36 +0000 (15:42 +0200)
committerSmallJoker <mk939@ymail.com>
Fri, 23 Aug 2019 17:17:43 +0000 (19:17 +0200)
src/client/clientmap.cpp
src/map.cpp
src/map.h

index f2db94296b194bdfd81d7d3b351a7cdd7ff58654..3e4ab2e94aefed7ebf7f048b16c8fac1bc66359c 100644 (file)
@@ -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 &sector_it : m_sectors) {
                MapSector *sector = sector_it.second;
                v2s16 sp = sector->getPos();
index 3b57a9493b7927cf03d0df7c10cd4014f7cc9dd8..6b027d34bc3d6844b38774eebe3e86ab39046353 100644 (file)
@@ -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<s16> &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<s16> 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;
index d26da37a60436465df9943808550fa58abd00278..232a25ac72fb9a10a08c6d5b91b296d8a8dae68c 100644 (file)
--- 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<s16> &block_bounds, float step, float stepfac,
+               float offset, u32 needed_count);
 
 private:
        f32 m_transforming_liquid_loop_count_multiplier = 1.0f;