From be39f61359ad63f2c6d4aea14c1dfd8357eee03f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?D=C3=A1niel=20Juh=C3=A1sz?= <juhdanad@gmail.com>
Date: Sun, 23 Oct 2016 17:51:13 +0200
Subject: [PATCH] Use node lighting for liquid spreading

This commit modifies the liquid transforming procedure to light and
unlight nodes instead of whole map blocks.
---
 src/map.cpp             |  23 +++-
 src/voxelalgorithms.cpp | 229 +++++++++++++++++++++-------------------
 src/voxelalgorithms.h   |  13 +--
 3 files changed, 144 insertions(+), 121 deletions(-)

diff --git a/src/map.cpp b/src/map.cpp
index 1694582db..5a1611c89 100644
--- a/src/map.cpp
+++ b/src/map.cpp
@@ -824,10 +824,15 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n,
 	}
 
 	// Set the node on the map
+	// Ignore light (because calling voxalgo::update_lighting_nodes)
+	n.setLight(LIGHTBANK_DAY, 0, ndef);
+	n.setLight(LIGHTBANK_NIGHT, 0, ndef);
 	setNode(p, n);
 
 	// Update lighting
-	voxalgo::update_lighting_node(this, ndef, p, oldnode, modified_blocks);
+	std::vector<std::pair<v3s16, MapNode> > oldnodes;
+	oldnodes.push_back(std::pair<v3s16, MapNode>(p, oldnode));
+	voxalgo::update_lighting_nodes(this, ndef, oldnodes, modified_blocks);
 
 	for(std::map<v3s16, MapBlock*>::iterator
 			i = modified_blocks.begin();
@@ -1224,7 +1229,9 @@ void Map::transformLiquids(std::map<v3s16, MapBlock*> &modified_blocks)
 	std::deque<v3s16> must_reflow;
 
 	// List of MapBlocks that will require a lighting update (due to lava)
-	std::map<v3s16, MapBlock *> lighting_modified_blocks;
+	std::map<v3s16, MapBlock *> lighting_modified_blocks2;
+
+	std::vector<std::pair<v3s16, MapNode> > changed_nodes;
 
 	u32 liquid_loop_max = g_settings->getS32("liquid_loop_max");
 	u32 loop_max = liquid_loop_max;
@@ -1457,6 +1464,10 @@ void Map::transformLiquids(std::map<v3s16, MapBlock*> &modified_blocks)
 		}
 		n0.setContent(new_node_content);
 
+		// Ignore light (because calling voxalgo::update_lighting_nodes)
+		n0.setLight(LIGHTBANK_DAY, 0, nodemgr);
+		n0.setLight(LIGHTBANK_NIGHT, 0, nodemgr);
+
 		// Find out whether there is a suspect for this action
 		std::string suspect;
 		if (m_gamedef->rollback())
@@ -1484,9 +1495,10 @@ void Map::transformLiquids(std::map<v3s16, MapBlock*> &modified_blocks)
 		if (block != NULL) {
 			modified_blocks[blockpos] =  block;
 			// If new or old node emits light, MapBlock requires lighting update
-			if (nodemgr->get(n0).light_source != 0 ||
+			/*if (nodemgr->get(n0).light_source != 0 ||
 					nodemgr->get(n00).light_source != 0)
-				lighting_modified_blocks[block->getPos()] = block;
+				lighting_modified_blocks[block->getPos()] = block;*/
+			changed_nodes.push_back(std::pair<v3s16, MapNode>(p0, n00));
 		}
 
 		/*
@@ -1515,7 +1527,8 @@ void Map::transformLiquids(std::map<v3s16, MapBlock*> &modified_blocks)
 	for (std::deque<v3s16>::iterator iter = must_reflow.begin(); iter != must_reflow.end(); ++iter)
 		m_transforming_liquid.push_back(*iter);
 
-	updateLighting(lighting_modified_blocks, modified_blocks);
+	//updateLighting(lighting_modified_blocks, modified_blocks);
+	voxalgo::update_lighting_nodes(this, nodemgr, changed_nodes, modified_blocks);
 
 
 	/* ----------------------------------------------------------------------
diff --git a/src/voxelalgorithms.cpp b/src/voxelalgorithms.cpp
index 019ec1bc6..83c1dd4ca 100644
--- a/src/voxelalgorithms.cpp
+++ b/src/voxelalgorithms.cpp
@@ -583,144 +583,153 @@ bool isSunlightAbove(Map *map, v3s16 pos, INodeDefManager *ndef)
 
 static const LightBank banks[] = { LIGHTBANK_DAY, LIGHTBANK_NIGHT };
 
-void update_lighting_node(Map *map, INodeDefManager *ndef, v3s16 p,
-	MapNode oldnode, std::map<v3s16, MapBlock*> & modified_blocks)
+void update_lighting_nodes(Map *map, INodeDefManager *ndef,
+	std::vector<std::pair<v3s16, MapNode> > &oldnodes,
+	std::map<v3s16, MapBlock*> & modified_blocks)
 {
 	// For node getter functions
 	bool is_valid_position;
 
-	// Get position and block of the changed node
-	relative_v3 rel_pos;
-	mapblock_v3 block_pos;
-	getNodeBlockPosWithOffset(p, block_pos, rel_pos);
-	MapBlock *block = map->getBlockNoCreateNoEx(block_pos);
-	if (block == NULL || block->isDummy()) {
-		return;
-	}
-
 	// Process each light bank separately
 	for (s32 i = 0; i < 2; i++) {
-		// Get the new node
-		MapNode n = block->getNodeNoCheck(rel_pos, &is_valid_position);
-		if (!is_valid_position) {
-			break;
-		}
 		LightBank bank = banks[i];
+		UnlightQueue disappearing_lights(256);
+		ReLightQueue light_sources(256);
+		// For each changed node process sunlight and initialize
+		for (std::vector<std::pair<v3s16, MapNode> >::iterator it =
+				oldnodes.begin(); it < oldnodes.end(); it++) {
+			// Get position and block of the changed node
+			v3s16 p = it->first;
+			relative_v3 rel_pos;
+			mapblock_v3 block_pos;
+			getNodeBlockPosWithOffset(p, block_pos, rel_pos);
+			MapBlock *block = map->getBlockNoCreateNoEx(block_pos);
+			if (block == NULL || block->isDummy()) {
+				continue;
+			}
+			// Get the new node
+			MapNode n = block->getNodeNoCheck(rel_pos, &is_valid_position);
+			if (!is_valid_position) {
+				break;
+			}
 
-		// Light of the old node
-		u8 old_light = oldnode.getLight(bank, ndef);
+			// Light of the old node
+			u8 old_light = it->second.getLight(bank, ndef);
 
-		// Add the block of the added node to modified_blocks
-		modified_blocks[block_pos] = block;
+			// Add the block of the added node to modified_blocks
+			modified_blocks[block_pos] = block;
 
-		// Get new light level of the node
-		u8 new_light = 0;
-		if (ndef->get(n).light_propagates) {
-			if (bank == LIGHTBANK_DAY && ndef->get(n).sunlight_propagates
+			// Get new light level of the node
+			u8 new_light = 0;
+			if (ndef->get(n).light_propagates) {
+				if (bank == LIGHTBANK_DAY && ndef->get(n).sunlight_propagates
 					&& isSunlightAbove(map, p, ndef)) {
-				new_light = LIGHT_SUN;
-			} else {
-				new_light = ndef->get(n).light_source;
-				for (int i = 0; i < 6; i++) {
-					v3s16 p2 = p + neighbor_dirs[i];
-					bool is_valid;
-					MapNode n2 = map->getNodeNoEx(p2, &is_valid);
-					if (is_valid) {
-						u8 spread = n2.getLight(bank, ndef);
-						// If the neighbor is at least as bright as
-						// this node then its light is not from
-						// this node.
-						// Its light can spread to this node.
-						if (spread > new_light && spread >= old_light) {
-							new_light = spread - 1;
+					new_light = LIGHT_SUN;
+				} else {
+					new_light = ndef->get(n).light_source;
+					for (int i = 0; i < 6; i++) {
+						v3s16 p2 = p + neighbor_dirs[i];
+						bool is_valid;
+						MapNode n2 = map->getNodeNoEx(p2, &is_valid);
+						if (is_valid) {
+							u8 spread = n2.getLight(bank, ndef);
+							// If the neighbor is at least as bright as
+							// this node then its light is not from
+							// this node.
+							// Its light can spread to this node.
+							if (spread > new_light && spread >= old_light) {
+								new_light = spread - 1;
+							}
 						}
 					}
 				}
+			} else {
+				// If this is an opaque node, it still can emit light.
+				new_light = ndef->get(n).light_source;
 			}
-		} else {
-			// If this is an opaque node, it still can emit light.
-			new_light = ndef->get(n).light_source;
-		}
-
-		ReLightQueue light_sources(256);
 
-		if (new_light > 0) {
-			light_sources.push(new_light, rel_pos, block_pos, block, 6);
-		}
+			if (new_light > 0) {
+				light_sources.push(new_light, rel_pos, block_pos, block, 6);
+			}
 
-		if (new_light < old_light) {
-			// The node became opaque or doesn't provide as much
-			// light as the previous one, so it must be unlighted.
-			LightQueue disappearing_lights(256);
+			if (new_light < old_light) {
+				// The node became opaque or doesn't provide as much
+				// light as the previous one, so it must be unlighted.
 
-			// Add to unlight queue
-			n.setLight(bank, 0, ndef);
-			block->setNodeNoCheck(rel_pos, n);
-			disappearing_lights.push(old_light, rel_pos, block_pos, block, 6);
+				// Add to unlight queue
+				n.setLight(bank, 0, ndef);
+				block->setNodeNoCheck(rel_pos, n);
+				disappearing_lights.push(old_light, rel_pos, block_pos, block,
+					6);
 
-			// Remove sunlight, if there was any
-			if (bank == LIGHTBANK_DAY && old_light == LIGHT_SUN) {
-				for (s16 y = p.Y - 1;; y--) {
-					v3s16 n2pos(p.X, y, p.Z);
+				// Remove sunlight, if there was any
+				if (bank == LIGHTBANK_DAY && old_light == LIGHT_SUN) {
+					for (s16 y = p.Y - 1;; y--) {
+						v3s16 n2pos(p.X, y, p.Z);
 
-					MapNode n2;
+						MapNode n2;
 
-					n2 = map->getNodeNoEx(n2pos, &is_valid_position);
-					if (!is_valid_position)
-						break;
+						n2 = map->getNodeNoEx(n2pos, &is_valid_position);
+						if (!is_valid_position)
+							break;
 
-					// If this node doesn't have sunlight, the nodes below
-					// it don't have too.
-					if (n2.getLight(LIGHTBANK_DAY, ndef) != LIGHT_SUN) {
-						break;
+						// If this node doesn't have sunlight, the nodes below
+						// it don't have too.
+						if (n2.getLight(LIGHTBANK_DAY, ndef) != LIGHT_SUN) {
+							break;
+						}
+						// Remove sunlight and add to unlight queue.
+						n2.setLight(LIGHTBANK_DAY, 0, ndef);
+						map->setNode(n2pos, n2);
+						relative_v3 rel_pos2;
+						mapblock_v3 block_pos2;
+						getNodeBlockPosWithOffset(n2pos, block_pos2, rel_pos2);
+						MapBlock *block2 = map->getBlockNoCreateNoEx(
+							block_pos2);
+						disappearing_lights.push(LIGHT_SUN, rel_pos2,
+							block_pos2, block2,
+							4 /* The node above caused the change */);
 					}
-					// Remove sunlight and add to unlight queue.
-					n2.setLight(LIGHTBANK_DAY, 0, ndef);
-					map->setNode(n2pos, n2);
-					relative_v3 rel_pos2;
-					mapblock_v3 block_pos2;
-					getNodeBlockPosWithOffset(n2pos, block_pos2, rel_pos2);
-					MapBlock *block2 = map->getBlockNoCreateNoEx(block_pos2);
-					disappearing_lights.push(LIGHT_SUN, rel_pos2, block_pos2,
-						block2, 4 /* The node above caused the change */);
 				}
-			}
-			// Remove lights
-			unspreadLight(map, ndef, bank, disappearing_lights, light_sources,
-				modified_blocks);
-		} else if (new_light > old_light) {
-			// It is sure that the node provides more light than the previous
-			// one, unlighting is not necessary.
-			// Propagate sunlight
-			if (bank == LIGHTBANK_DAY && new_light == LIGHT_SUN) {
-				for (s16 y = p.Y - 1;; y--) {
-					v3s16 n2pos(p.X, y, p.Z);
-
-					MapNode n2;
-
-					n2 = map->getNodeNoEx(n2pos, &is_valid_position);
-					if (!is_valid_position)
-						break;
-
-					// This should not happen, but if the node has sunlight
-					// then the iteration should stop.
-					if (n2.getLight(LIGHTBANK_DAY, ndef) == LIGHT_SUN) {
-						break;
-					}
-					// If the node terminates sunlight, stop.
-					if (!ndef->get(n2).sunlight_propagates) {
-						break;
+			} else if (new_light > old_light) {
+				// It is sure that the node provides more light than the previous
+				// one, unlighting is not necessary.
+				// Propagate sunlight
+				if (bank == LIGHTBANK_DAY && new_light == LIGHT_SUN) {
+					for (s16 y = p.Y - 1;; y--) {
+						v3s16 n2pos(p.X, y, p.Z);
+
+						MapNode n2;
+
+						n2 = map->getNodeNoEx(n2pos, &is_valid_position);
+						if (!is_valid_position)
+							break;
+
+						// This should not happen, but if the node has sunlight
+						// then the iteration should stop.
+						if (n2.getLight(LIGHTBANK_DAY, ndef) == LIGHT_SUN) {
+							break;
+						}
+						// If the node terminates sunlight, stop.
+						if (!ndef->get(n2).sunlight_propagates) {
+							break;
+						}
+						relative_v3 rel_pos2;
+						mapblock_v3 block_pos2;
+						getNodeBlockPosWithOffset(n2pos, block_pos2, rel_pos2);
+						MapBlock *block2 = map->getBlockNoCreateNoEx(
+							block_pos2);
+						// Mark node for lighting.
+						light_sources.push(LIGHT_SUN, rel_pos2, block_pos2,
+							block2, 4);
 					}
-					relative_v3 rel_pos2;
-					mapblock_v3 block_pos2;
-					getNodeBlockPosWithOffset(n2pos, block_pos2, rel_pos2);
-					MapBlock *block2 = map->getBlockNoCreateNoEx(block_pos2);
-					// Mark node for lighting.
-					light_sources.push(LIGHT_SUN, rel_pos2, block_pos2, block2,
-						4);
 				}
 			}
+
 		}
+		// Remove lights
+		unspreadLight(map, ndef, bank, disappearing_lights, light_sources,
+			modified_blocks);
 		// Initialize light values for light spreading.
 		for (u8 i = 0; i <= LIGHT_SUN; i++) {
 			const std::vector<ChangingLight> &lights = light_sources.lights[i];
diff --git a/src/voxelalgorithms.h b/src/voxelalgorithms.h
index f2b2fde32..3632546dd 100644
--- a/src/voxelalgorithms.h
+++ b/src/voxelalgorithms.h
@@ -58,18 +58,19 @@ SunlightPropagateResult propagateSunlight(VoxelManipulator &v, VoxelArea a,
 /*!
  * Updates the lighting on the map.
  * The result will be correct only if
- * no nodes were changed except the given one.
+ * no nodes were changed except the given ones.
+ * Before calling this procedure make sure that all new nodes on
+ * the map have zero light level!
  *
- * \param p position of the changed node
- * \param oldnode this node was overwritten on the map
+ * \param oldnodes contains the MapNodes that were replaced by the new
+ * MapNodes and their positions
  * \param modified_blocks output, contains all map blocks that
  * the function modified
  */
-void update_lighting_node(
+void update_lighting_nodes(
 	Map *map,
 	INodeDefManager *ndef,
-	v3s16 p,
-	MapNode oldnode,
+	std::vector<std::pair<v3s16, MapNode> > &oldnodes,
 	std::map<v3s16, MapBlock*> &modified_blocks);
 
 } // namespace voxalgo
-- 
GitLab