Smooth lighting: Fix light leaking through edge-connected corners
authorDTA7 <dta7e@t-online.de>
Mon, 2 Oct 2017 23:23:49 +0000 (01:23 +0200)
committerparamat <mat.gregory@virginmedia.com>
Tue, 10 Oct 2017 18:53:13 +0000 (19:53 +0100)
For solid nodes, the lighting at a corner becomes face-dependent, which
means that only the four nodes in face-direction contribute to the
lighting (getSmoothLightSolid).
For special nodes, which use the lighting values at the eight corners of
a node, the lighting is not face-dependent, but certain nodes of the
eight surrounding nodes of a corner (here indices 4, 5, 6 and 7) can be
obstructed.

Ambient occlusion now also occurs for solid nodes, if two, three or four
of the four nodes in face-direction are solid.

src/content_mapblock.cpp
src/mapblock_mesh.cpp
src/mapblock_mesh.h

index 577fbc5f8b575b2e4d770adfa9d32177f4b5e0b9..4e32e27caceee1802ba7af78fbe0c416b2d5f31d 100644 (file)
@@ -276,7 +276,7 @@ void MapblockMeshGenerator::drawCuboid(const aabb3f &box,
 void MapblockMeshGenerator::getSmoothLightFrame()
 {
        for (int k = 0; k < 8; ++k) {
-               u16 light = getSmoothLight(blockpos_nodes + p, light_dirs[k], data);
+               u16 light = getSmoothLightTransparent(blockpos_nodes + p, light_dirs[k], data);
                frame.lightsA[k] = light & 0xff;
                frame.lightsB[k] = light >> 8;
        }
index dc37c0c5fc24d6abc290c3b1c1c521f1c180eee8..7708fb43878e60779130bd0c187374cea16ddc21 100644 (file)
@@ -28,6 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "content_mapblock.h"
 #include "util/directiontables.h"
 #include "client/renderingengine.h"
+#include <array>
 
 /*
        MeshMakeData
@@ -68,7 +69,7 @@ void MeshMakeData::fill(MapBlock *block)
 
        fillBlockData(v3s16(0,0,0), block->getData());
 
-       // Get map for reading neigbhor blocks
+       // Get map for reading neighbor blocks
        Map *map = block->getParent();
 
        for (const v3s16 &dir : g_26dirs) {
@@ -194,19 +195,9 @@ u16 getFaceLight(MapNode n, MapNode n2, v3s16 face_dir, INodeDefManager *ndef)
        Calculate smooth lighting at the XYZ- corner of p.
        Both light banks
 */
-static u16 getSmoothLightCombined(const v3s16 &p, MeshMakeData *data)
+static u16 getSmoothLightCombined(const v3s16 &p,
+       const std::array<v3s16,8> &dirs, MeshMakeData *data, bool node_solid)
 {
-       static const v3s16 dirs8[8] = {
-               v3s16(0,0,0),
-               v3s16(0,0,1),
-               v3s16(0,1,0),
-               v3s16(0,1,1),
-               v3s16(1,0,0),
-               v3s16(1,1,0),
-               v3s16(1,0,1),
-               v3s16(1,1,1),
-       };
-
        INodeDefManager *ndef = data->m_client->ndef();
 
        u16 ambient_occlusion = 0;
@@ -215,32 +206,58 @@ static u16 getSmoothLightCombined(const v3s16 &p, MeshMakeData *data)
        u16 light_day = 0;
        u16 light_night = 0;
 
-       for (const v3s16 &dir : dirs8) {
-               MapNode n = data->m_vmanip.getNodeNoExNoEmerge(p - dir);
-
-               // if it's CONTENT_IGNORE we can't do any light calculations
-               if (n.getContent() == CONTENT_IGNORE)
-                       continue;
-
+       auto add_node = [&] (int i) -> const ContentFeatures& {
+               MapNode n = data->m_vmanip.getNodeNoExNoEmerge(p + dirs[i]);
                const ContentFeatures &f = ndef->get(n);
                if (f.light_source > light_source_max)
                        light_source_max = f.light_source;
                // Check f.solidness because fast-style leaves look better this way
                if (f.param_type == CPT_LIGHT && f.solidness != 2) {
                        light_day += decode_light(n.getLightNoChecks(LIGHTBANK_DAY, &f));
-                       light_night += decode_light(
-                               n.getLightNoChecks(LIGHTBANK_NIGHT, &f));
+                       light_night += decode_light(n.getLightNoChecks(LIGHTBANK_NIGHT, &f));
                        light_count++;
                } else {
                        ambient_occlusion++;
                }
-       }
+               return f;
+       };
 
-       if(light_count == 0)
-               return 0xffff;
+       if (node_solid) {
+               ambient_occlusion = 3;
+               bool corner_obstructed = true;
+               for (int i = 0; i < 2; ++i) {
+                       if (add_node(i).light_propagates)
+                               corner_obstructed = false;
+               }
+               add_node(2);
+               add_node(3);
+               if (corner_obstructed)
+                       ambient_occlusion++;
+               else
+                       add_node(4);
+       } else {
+               std::array<bool, 4> obstructed = {{ 1, 1, 1, 1 }};
+               add_node(0);
+               bool opaque1 = !add_node(1).light_propagates;
+               bool opaque2 = !add_node(2).light_propagates;
+               bool opaque3 = !add_node(3).light_propagates;
+               obstructed[0] = opaque1 && opaque2;
+               obstructed[1] = opaque1 && opaque3;
+               obstructed[2] = opaque2 && opaque3;
+               for (int k = 0; k < 4; ++k) {
+                       if (obstructed[k])
+                               ambient_occlusion++;
+                       else if (add_node(k + 4).light_propagates)
+                               obstructed[3] = false;
+               }
+       }
 
-       light_day /= light_count;
-       light_night /= light_count;
+       if (light_count == 0) {
+               light_day = light_night = 0;
+       } else {
+               light_day /= light_count;
+               light_night /= light_count;
+       }
 
        // Boost brightness around light sources
        bool skip_ambient_occlusion_day = false;
@@ -283,20 +300,70 @@ static u16 getSmoothLightCombined(const v3s16 &p, MeshMakeData *data)
 /*
        Calculate smooth lighting at the given corner of p.
        Both light banks.
+       Node at p is solid, and thus the lighting is face-dependent.
 */
-u16 getSmoothLight(v3s16 p, v3s16 corner, MeshMakeData *data)
+u16 getSmoothLightSolid(const v3s16 &p, const v3s16 &face_dir, const v3s16 &corner, MeshMakeData *data)
 {
-       if (corner.X == 1)
-               ++p.X;
-       // else corner.X == -1
-       if (corner.Y == 1)
-               ++p.Y;
-       // else corner.Y == -1
-       if (corner.Z == 1)
-               ++p.Z;
-       // else corner.Z == -1
-
-       return getSmoothLightCombined(p, data);
+       v3s16 neighbor_offset1, neighbor_offset2;
+
+       /*
+        * face_dir, neighbor_offset1 and neighbor_offset2 define an
+        * orthonormal basis which is used to define the offsets of the 8
+        * surrounding nodes and to differentiate the "distance" (by going only
+        * along directly neighboring nodes) relative to the node at p.
+        * Apart from the node at p, only the 4 nodes which contain face_dir
+        * can contribute light.
+        */
+       if (face_dir.X != 0) {
+               neighbor_offset1 = v3s16(0, corner.Y, 0);
+               neighbor_offset2 = v3s16(0, 0, corner.Z);
+       } else if (face_dir.Y != 0) {
+               neighbor_offset1 = v3s16(0, 0, corner.Z);
+               neighbor_offset2 = v3s16(corner.X, 0, 0);
+       } else if (face_dir.Z != 0) {
+               neighbor_offset1 = v3s16(corner.X,0,0);
+               neighbor_offset2 = v3s16(0,corner.Y,0);
+       }
+
+       const std::array<v3s16,8> dirs = {{
+               // Always shine light
+               neighbor_offset1 + face_dir,
+               neighbor_offset2 + face_dir,
+               v3s16(0,0,0),
+               face_dir,
+
+               // Can be obstructed
+               neighbor_offset1 + neighbor_offset2 + face_dir,
+
+               // Do not shine light, only for ambient occlusion
+               neighbor_offset1,
+               neighbor_offset2,
+               neighbor_offset1 + neighbor_offset2
+       }};
+       return getSmoothLightCombined(p, dirs, data, true);
+}
+
+/*
+       Calculate smooth lighting at the given corner of p.
+       Both light banks.
+       Node at p is not solid, and the lighting is not face-dependent.
+*/
+u16 getSmoothLightTransparent(const v3s16 &p, const v3s16 &corner, MeshMakeData *data)
+{
+       const std::array<v3s16,8> dirs = {{
+               // Always shine light
+               v3s16(0,0,0),
+               v3s16(corner.X,0,0),
+               v3s16(0,corner.Y,0),
+               v3s16(0,0,corner.Z),
+
+               // Can be obstructed
+               v3s16(corner.X,corner.Y,0),
+               v3s16(corner.X,0,corner.Z),
+               v3s16(0,corner.Y,corner.Z),
+               v3s16(corner.X,corner.Y,corner.Z)
+       }};
+       return getSmoothLightCombined(p, dirs, data, false);
 }
 
 void get_sunlight_color(video::SColorf *sunlight, u32 daynight_ratio){
@@ -816,7 +883,7 @@ static void getTileInfo(
 
                v3s16 light_p = blockpos_nodes + p_corrected;
                for (u16 i = 0; i < 4; i++)
-                       lights[i] = getSmoothLight(light_p, vertex_dirs[i], data);
+                       lights[i] = getSmoothLightSolid(light_p, face_dir_corrected, vertex_dirs[i], data);
        }
 }
 
index b4bfedb848e5d70ef566bd347c688d4adf357c6a..13cee537c5e5cf736ae076a55bd362a10c01aea7 100644 (file)
@@ -232,7 +232,8 @@ video::SColor encode_light(u16 light, u8 emissive_light);
 // Compute light at node
 u16 getInteriorLight(MapNode n, s32 increment, INodeDefManager *ndef);
 u16 getFaceLight(MapNode n, MapNode n2, v3s16 face_dir, INodeDefManager *ndef);
-u16 getSmoothLight(v3s16 p, v3s16 corner, MeshMakeData *data);
+u16 getSmoothLightSolid(const v3s16 &p, const v3s16 &face_dir, const v3s16 &corner, MeshMakeData *data);
+u16 getSmoothLightTransparent(const v3s16 &p, const v3s16 &corner, MeshMakeData *data);
 
 /*!
  * Returns the sunlight's color from the current