Improve occlusion culling in corridors with additional check
authorsfan5 <sfan5@live.de>
Thu, 22 Aug 2019 22:14:45 +0000 (00:14 +0200)
committersfan5 <sfan5@live.de>
Sat, 24 Aug 2019 16:37:25 +0000 (18:37 +0200)
src/map.cpp
src/map.h

index fba970823f887cc89c615c773893f431f8b694da..022eb9f198290190a159a57af7c095f9fd7c686f 100644 (file)
@@ -1032,6 +1032,70 @@ void Map::removeNodeTimer(v3s16 p)
        block->m_node_timers.remove(p_rel);
 }
 
+bool Map::determineAdditionalOcclusionCheck(const v3s16 &pos_camera,
+       const core::aabbox3d<s16> &block_bounds, v3s16 &check)
+{
+       /*
+               This functions determines the node inside the target block that is
+               closest to the camera position. This increases the occlusion culling
+               accuracy in straight and diagonal corridors.
+               The returned position will be occlusion checked first in addition to the
+               others (8 corners + center).
+               No position is returned if
+               - the closest node is a corner, corners are checked anyway.
+               - the camera is inside the target block, it will never be occluded.
+       */
+#define CLOSEST_EDGE(pos, bounds, axis) \
+       ((pos).axis <= (bounds).MinEdge.axis) ? (bounds).MinEdge.axis : \
+       (bounds).MaxEdge.axis
+
+       bool x_inside = (block_bounds.MinEdge.X <= pos_camera.X) &&
+                       (pos_camera.X <= block_bounds.MaxEdge.X);
+       bool y_inside = (block_bounds.MinEdge.Y <= pos_camera.Y) &&
+                       (pos_camera.Y <= block_bounds.MaxEdge.Y);
+       bool z_inside = (block_bounds.MinEdge.Z <= pos_camera.Z) &&
+                       (pos_camera.Z <= block_bounds.MaxEdge.Z);
+
+       if (x_inside && y_inside && z_inside)
+               return false; // Camera inside target mapblock
+
+       // straight
+       if (x_inside && y_inside) {
+               check = v3s16(pos_camera.X, pos_camera.Y, 0);
+               check.Z = CLOSEST_EDGE(pos_camera, block_bounds, Z);
+               return true;
+       } else if (y_inside && z_inside) {
+               check = v3s16(0, pos_camera.Y, pos_camera.Z);
+               check.X = CLOSEST_EDGE(pos_camera, block_bounds, X);
+               return true;
+       } else if (x_inside && z_inside) {
+               check = v3s16(pos_camera.X, 0, pos_camera.Z);
+               check.Y = CLOSEST_EDGE(pos_camera, block_bounds, Y);
+               return true;
+       }
+
+       // diagonal
+       if (x_inside) {
+               check = v3s16(pos_camera.X, 0, 0);
+               check.Y = CLOSEST_EDGE(pos_camera, block_bounds, Y);
+               check.Z = CLOSEST_EDGE(pos_camera, block_bounds, Z);
+               return true;
+       } else if (y_inside) {
+               check = v3s16(0, pos_camera.Y, 0);
+               check.X = CLOSEST_EDGE(pos_camera, block_bounds, X);
+               check.Z = CLOSEST_EDGE(pos_camera, block_bounds, Z);
+               return true;
+       } else if (z_inside) {
+               check = v3s16(0, 0, pos_camera.Z);
+               check.X = CLOSEST_EDGE(pos_camera, block_bounds, X);
+               check.Y = CLOSEST_EDGE(pos_camera, block_bounds, Y);
+               return true;
+       }
+
+       // Closest node would be a corner, none returned
+       return false;
+}
+
 bool Map::isOccluded(const v3s16 &pos_camera, const v3s16 &pos_target,
        float step, float stepfac, float offset, float end_offset, u32 needed_count)
 {
@@ -1102,6 +1166,15 @@ bool Map::isBlockOccluded(MapBlock *block, v3s16 cam_pos_nodes)
        // this is a HACK, we should think of a more precise algorithm
        u32 needed_count = 2;
 
+       // Additional occlusion check, see comments in that function
+       v3s16 check;
+       if (determineAdditionalOcclusionCheck(cam_pos_nodes, block->getBox(), check)) {
+               // node is always on a side facing the camera, end_offset can be lower
+               if (!isOccluded(cam_pos_nodes, check, step, stepfac, start_offset,
+                               -1.0f, needed_count))
+                       return false;
+       }
+
        for (const v3s16 &dir : dir9) {
                if (!isOccluded(cam_pos_nodes, pos_blockcenter + dir, step, stepfac,
                                start_offset, end_offset, needed_count))
index 8330a7a5f335b229ff190819b5f967bea73cfedf..67e00c6f4d1943066c6fd6ac06a78c0e65ce0088 100644 (file)
--- a/src/map.h
+++ b/src/map.h
@@ -310,6 +310,8 @@ protected:
        // This stores the properties of the nodes on the map.
        const NodeDefManager *m_nodedef;
 
+       bool determineAdditionalOcclusionCheck(const v3s16 &pos_camera,
+               const core::aabbox3d<s16> &block_bounds, v3s16 &check);
        bool isOccluded(const v3s16 &pos_camera, const v3s16 &pos_target,
                float step, float stepfac, float start_offset, float end_offset,
                u32 needed_count);