diff options
author | DTA7 <dta7e@t-online.de> | 2017-10-03 01:23:49 +0200 |
---|---|---|
committer | paramat <mat.gregory@virginmedia.com> | 2017-10-10 19:53:13 +0100 |
commit | 2cf9014160085303cda333334784ac4938edc086 (patch) | |
tree | 328108961d24b8b710f5f17d2f0b78a8d68550e4 | |
parent | 1ff5c204425401c83a8a4c662795be64c5a3c1df (diff) | |
download | minetest-2cf9014160085303cda333334784ac4938edc086.tar.gz minetest-2cf9014160085303cda333334784ac4938edc086.tar.bz2 minetest-2cf9014160085303cda333334784ac4938edc086.zip |
Smooth lighting: Fix light leaking through edge-connected corners
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.
-rw-r--r-- | src/content_mapblock.cpp | 2 | ||||
-rw-r--r-- | src/mapblock_mesh.cpp | 147 | ||||
-rw-r--r-- | src/mapblock_mesh.h | 3 |
3 files changed, 110 insertions, 42 deletions
diff --git a/src/content_mapblock.cpp b/src/content_mapblock.cpp index 577fbc5f8..4e32e27ca 100644 --- a/src/content_mapblock.cpp +++ b/src/content_mapblock.cpp @@ -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; } diff --git a/src/mapblock_mesh.cpp b/src/mapblock_mesh.cpp index dc37c0c5f..7708fb438 100644 --- a/src/mapblock_mesh.cpp +++ b/src/mapblock_mesh.cpp @@ -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); } } diff --git a/src/mapblock_mesh.h b/src/mapblock_mesh.h index b4bfedb84..13cee537c 100644 --- a/src/mapblock_mesh.h +++ b/src/mapblock_mesh.h @@ -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 |