]> git.lizzy.rs Git - minetest.git/blob - src/voxelalgorithms.cpp
Android: Increase minimum SDK version to 21
[minetest.git] / src / voxelalgorithms.cpp
1 /*
2 Minetest
3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "voxelalgorithms.h"
21 #include "nodedef.h"
22 #include "mapblock.h"
23 #include "map.h"
24
25 namespace voxalgo
26 {
27
28 /*!
29  * A direction.
30  * 0=X+
31  * 1=Y+
32  * 2=Z+
33  * 3=Z-
34  * 4=Y-
35  * 5=X-
36  * 6=no direction
37  * Two directions are opposite only if their sum is 5.
38  */
39 typedef u8 direction;
40 /*!
41  * Relative node position.
42  * This represents a node's position in its map block.
43  * All coordinates must be between 0 and 15.
44  */
45 typedef v3s16 relative_v3;
46 /*!
47  * Position of a map block (block coordinates).
48  * One block_pos unit is as long as 16 node position units.
49  */
50 typedef v3s16 mapblock_v3;
51
52 //! Contains information about a node whose light is about to change.
53 struct ChangingLight {
54         //! Relative position of the node in its map block.
55         relative_v3 rel_position;
56         //! Position of the node's block.
57         mapblock_v3 block_position;
58         //! Pointer to the node's block.
59         MapBlock *block = NULL;
60         /*!
61          * Direction from the node that caused this node's changing
62          * to this node.
63          */
64         direction source_direction = 6;
65
66         ChangingLight() = default;
67
68         ChangingLight(relative_v3 rel_pos, mapblock_v3 block_pos,
69                 MapBlock *b, direction source_dir) :
70                 rel_position(rel_pos),
71                 block_position(block_pos),
72                 block(b),
73                 source_direction(source_dir)
74         {}
75 };
76
77 /*!
78  * A fast, priority queue-like container to contain ChangingLights.
79  * The ChangingLights are ordered by the given light levels.
80  * The brightest ChangingLight is returned first.
81  */
82 struct LightQueue {
83         //! For each light level there is a vector.
84         std::vector<ChangingLight> lights[LIGHT_SUN + 1];
85         //! Light of the brightest ChangingLight in the queue.
86         u8 max_light;
87
88         /*!
89          * Creates a LightQueue.
90          * \param reserve for each light level that many slots are reserved.
91          */
92         LightQueue(size_t reserve)
93         {
94                 max_light = LIGHT_SUN;
95                 for (u8 i = 0; i <= LIGHT_SUN; i++) {
96                         lights[i].reserve(reserve);
97                 }
98         }
99
100         /*!
101          * Returns the next brightest ChangingLight and
102          * removes it from the queue.
103          * If there were no elements in the queue, the given parameters
104          * remain unmodified.
105          * \param light light level of the popped ChangingLight
106          * \param data the ChangingLight that was popped
107          * \returns true if there was a ChangingLight in the queue.
108          */
109         bool next(u8 &light, ChangingLight &data)
110         {
111                 while (lights[max_light].empty()) {
112                         if (max_light == 0) {
113                                 return false;
114                         }
115                         max_light--;
116                 }
117                 light = max_light;
118                 data = lights[max_light].back();
119                 lights[max_light].pop_back();
120                 return true;
121         }
122
123         /*!
124          * Adds an element to the queue.
125          * The parameters are the same as in ChangingLight's constructor.
126          * \param light light level of the ChangingLight
127          */
128         inline void push(u8 light, relative_v3 rel_pos,
129                 mapblock_v3 block_pos, MapBlock *block,
130                 direction source_dir)
131         {
132                 assert(light <= LIGHT_SUN);
133                 lights[light].emplace_back(rel_pos, block_pos, block, source_dir);
134         }
135 };
136
137 /*!
138  * This type of light queue is for unlighting.
139  * A node can be pushed in it only if its raw light is zero.
140  * This prevents pushing nodes twice into this queue.
141  * The light of the pushed ChangingLight must be the
142  * light of the node before unlighting it.
143  */
144 typedef LightQueue UnlightQueue;
145 /*!
146  * This type of light queue is for spreading lights.
147  * While spreading lights, all the nodes in it must
148  * have the same light as the light level the ChangingLights
149  * were pushed into this queue with. This prevents unnecessary
150  * re-pushing of the nodes into the queue.
151  * If a node doesn't let light trough but emits light, it can be added
152  * too.
153  */
154 typedef LightQueue ReLightQueue;
155
156 /*!
157  * neighbor_dirs[i] points towards
158  * the direction i.
159  * See the definition of the type "direction"
160  */
161 const static v3s16 neighbor_dirs[6] = {
162         v3s16(1, 0, 0), // right
163         v3s16(0, 1, 0), // top
164         v3s16(0, 0, 1), // back
165         v3s16(0, 0, -1), // front
166         v3s16(0, -1, 0), // bottom
167         v3s16(-1, 0, 0), // left
168 };
169
170 /*!
171  * Transforms the given map block offset by one node towards
172  * the specified direction.
173  * \param dir the direction of the transformation
174  * \param rel_pos the node's relative position in its map block
175  * \param block_pos position of the node's block
176  */
177 bool step_rel_block_pos(direction dir, relative_v3 &rel_pos,
178         mapblock_v3 &block_pos)
179 {
180         switch (dir) {
181         case 0:
182                 if (rel_pos.X < MAP_BLOCKSIZE - 1) {
183                         rel_pos.X++;
184                 } else {
185                         rel_pos.X = 0;
186                         block_pos.X++;
187                         return true;
188                 }
189                 break;
190         case 1:
191                 if (rel_pos.Y < MAP_BLOCKSIZE - 1) {
192                         rel_pos.Y++;
193                 } else {
194                         rel_pos.Y = 0;
195                         block_pos.Y++;
196                         return true;
197                 }
198                 break;
199         case 2:
200                 if (rel_pos.Z < MAP_BLOCKSIZE - 1) {
201                         rel_pos.Z++;
202                 } else {
203                         rel_pos.Z = 0;
204                         block_pos.Z++;
205                         return true;
206                 }
207                 break;
208         case 3:
209                 if (rel_pos.Z > 0) {
210                         rel_pos.Z--;
211                 } else {
212                         rel_pos.Z = MAP_BLOCKSIZE - 1;
213                         block_pos.Z--;
214                         return true;
215                 }
216                 break;
217         case 4:
218                 if (rel_pos.Y > 0) {
219                         rel_pos.Y--;
220                 } else {
221                         rel_pos.Y = MAP_BLOCKSIZE - 1;
222                         block_pos.Y--;
223                         return true;
224                 }
225                 break;
226         case 5:
227                 if (rel_pos.X > 0) {
228                         rel_pos.X--;
229                 } else {
230                         rel_pos.X = MAP_BLOCKSIZE - 1;
231                         block_pos.X--;
232                         return true;
233                 }
234                 break;
235         }
236         return false;
237 }
238
239 /*
240  * Removes all light that is potentially emitted by the specified
241  * light sources. These nodes will have zero light.
242  * Returns all nodes whose light became zero but should be re-lighted.
243  *
244  * \param bank the light bank in which the procedure operates
245  * \param from_nodes nodes whose light is removed
246  * \param light_sources nodes that should be re-lighted
247  * \param modified_blocks output, all modified map blocks are added to this
248  */
249 void unspread_light(Map *map, const NodeDefManager *nodemgr, LightBank bank,
250         UnlightQueue &from_nodes, ReLightQueue &light_sources,
251         std::map<v3s16, MapBlock*> &modified_blocks)
252 {
253         // Stores data popped from from_nodes
254         u8 current_light;
255         ChangingLight current;
256         // Data of the current neighbor
257         mapblock_v3 neighbor_block_pos;
258         relative_v3 neighbor_rel_pos;
259         // Direction of the brightest neighbor of the node
260         direction source_dir;
261         while (from_nodes.next(current_light, current)) {
262                 // For all nodes that need unlighting
263
264                 // There is no brightest neighbor
265                 source_dir = 6;
266                 // The current node
267                 const MapNode &node = current.block->getNodeNoCheck(current.rel_position);
268                 ContentLightingFlags f = nodemgr->getLightingFlags(node);
269                 // If the node emits light, it behaves like it had a
270                 // brighter neighbor.
271                 u8 brightest_neighbor_light = f.light_source + 1;
272                 for (direction i = 0; i < 6; i++) {
273                         //For each neighbor
274
275                         // The node that changed this node has already zero light
276                         // and it can't give light to this node
277                         if (current.source_direction + i == 5) {
278                                 continue;
279                         }
280                         // Get the neighbor's position and block
281                         neighbor_rel_pos = current.rel_position;
282                         neighbor_block_pos = current.block_position;
283                         MapBlock *neighbor_block;
284                         if (step_rel_block_pos(i, neighbor_rel_pos, neighbor_block_pos)) {
285                                 neighbor_block = map->getBlockNoCreateNoEx(neighbor_block_pos);
286                                 if (neighbor_block == NULL) {
287                                         current.block->setLightingComplete(bank, i, false);
288                                         continue;
289                                 }
290                         } else {
291                                 neighbor_block = current.block;
292                         }
293                         // Get the neighbor itself
294                         MapNode neighbor = neighbor_block->getNodeNoCheck(neighbor_rel_pos);
295                         ContentLightingFlags neighbor_f = nodemgr->getLightingFlags(
296                                 neighbor.getContent());
297                         u8 neighbor_light = neighbor.getLightRaw(bank, neighbor_f);
298                         // If the neighbor has at least as much light as this node, then
299                         // it won't lose its light, since it should have been added to
300                         // from_nodes earlier, so its light would be zero.
301                         if (neighbor_f.light_propagates && neighbor_light < current_light) {
302                                 // Unlight, but only if the node has light.
303                                 if (neighbor_light > 0) {
304                                         neighbor.setLight(bank, 0, neighbor_f);
305                                         neighbor_block->setNodeNoCheck(neighbor_rel_pos, neighbor);
306                                         from_nodes.push(neighbor_light, neighbor_rel_pos,
307                                                 neighbor_block_pos, neighbor_block, i);
308                                         // The current node was modified earlier, so its block
309                                         // is in modified_blocks.
310                                         if (current.block != neighbor_block) {
311                                                 modified_blocks[neighbor_block_pos] = neighbor_block;
312                                         }
313                                 }
314                         } else {
315                                 // The neighbor can light up this node.
316                                 if (neighbor_light < neighbor_f.light_source) {
317                                         neighbor_light = neighbor_f.light_source;
318                                 }
319                                 if (brightest_neighbor_light < neighbor_light) {
320                                         brightest_neighbor_light = neighbor_light;
321                                         source_dir = i;
322                                 }
323                         }
324                 }
325                 // If the brightest neighbor is able to light up this node,
326                 // then add this node to the output nodes.
327                 if (brightest_neighbor_light > 1 && f.light_propagates) {
328                         brightest_neighbor_light--;
329                         light_sources.push(brightest_neighbor_light, current.rel_position,
330                                 current.block_position, current.block,
331                                 (source_dir == 6) ? 6 : 5 - source_dir
332                                 /* with opposite direction*/);
333                 }
334         }
335 }
336
337 /*
338  * Spreads light from the specified starting nodes.
339  *
340  * Before calling this procedure, make sure that all ChangingLights
341  * in light_sources have as much light on the map as they have in
342  * light_sources (if the queue contains a node multiple times, the brightest
343  * occurrence counts).
344  *
345  * \param bank the light bank in which the procedure operates
346  * \param light_sources starting nodes
347  * \param modified_blocks output, all modified map blocks are added to this
348  */
349 void spread_light(Map *map, const NodeDefManager *nodemgr, LightBank bank,
350         LightQueue &light_sources,
351         std::map<v3s16, MapBlock*> &modified_blocks)
352 {
353         // The light the current node can provide to its neighbors.
354         u8 spreading_light;
355         // The ChangingLight for the current node.
356         ChangingLight current;
357         // Position of the current neighbor.
358         mapblock_v3 neighbor_block_pos;
359         relative_v3 neighbor_rel_pos;
360         while (light_sources.next(spreading_light, current)) {
361                 spreading_light--;
362                 for (direction i = 0; i < 6; i++) {
363                         // This node can't light up its light source
364                         if (current.source_direction + i == 5) {
365                                 continue;
366                         }
367                         // Get the neighbor's position and block
368                         neighbor_rel_pos = current.rel_position;
369                         neighbor_block_pos = current.block_position;
370                         MapBlock *neighbor_block;
371                         if (step_rel_block_pos(i, neighbor_rel_pos, neighbor_block_pos)) {
372                                 neighbor_block = map->getBlockNoCreateNoEx(neighbor_block_pos);
373                                 if (neighbor_block == NULL) {
374                                         current.block->setLightingComplete(bank, i, false);
375                                         continue;
376                                 }
377                         } else {
378                                 neighbor_block = current.block;
379                         }
380                         // Get the neighbor itself
381                         MapNode neighbor = neighbor_block->getNodeNoCheck(neighbor_rel_pos);
382                         ContentLightingFlags f = nodemgr->getLightingFlags(neighbor);
383                         if (f.light_propagates) {
384                                 // Light up the neighbor, if it has less light than it should.
385                                 u8 neighbor_light = neighbor.getLightRaw(bank, f);
386                                 if (neighbor_light < spreading_light) {
387                                         neighbor.setLight(bank, spreading_light, f);
388                                         neighbor_block->setNodeNoCheck(neighbor_rel_pos, neighbor);
389                                         light_sources.push(spreading_light, neighbor_rel_pos,
390                                                 neighbor_block_pos, neighbor_block, i);
391                                         // The current node was modified earlier, so its block
392                                         // is in modified_blocks.
393                                         if (current.block != neighbor_block) {
394                                                 modified_blocks[neighbor_block_pos] = neighbor_block;
395                                         }
396                                 }
397                         }
398                 }
399         }
400 }
401
402 struct SunlightPropagationUnit{
403         v2s16 relative_pos;
404         bool is_sunlit;
405
406         SunlightPropagationUnit(v2s16 relpos, bool sunlit):
407                 relative_pos(relpos),
408                 is_sunlit(sunlit)
409         {}
410 };
411
412 struct SunlightPropagationData{
413         std::vector<SunlightPropagationUnit> data;
414         v3s16 target_block;
415 };
416
417 /*!
418  * Returns true if the node gets sunlight from the
419  * node above it.
420  *
421  * \param pos position of the node.
422  */
423 bool is_sunlight_above(Map *map, v3s16 pos, const NodeDefManager *ndef)
424 {
425         bool sunlight = true;
426         mapblock_v3 source_block_pos;
427         relative_v3 source_rel_pos;
428         getNodeBlockPosWithOffset(pos + v3s16(0, 1, 0), source_block_pos,
429                 source_rel_pos);
430         // If the node above has sunlight, this node also can get it.
431         MapBlock *source_block = map->getBlockNoCreateNoEx(source_block_pos);
432         if (source_block == NULL) {
433                 // But if there is no node above, then use heuristics
434                 MapBlock *node_block = map->getBlockNoCreateNoEx(getNodeBlockPos(pos));
435                 if (node_block == NULL) {
436                         sunlight = false;
437                 } else {
438                         sunlight = !node_block->getIsUnderground();
439                 }
440         } else {
441                 MapNode above = source_block->getNodeNoCheck(source_rel_pos);
442                 if (above.getContent() == CONTENT_IGNORE) {
443                         // Trust heuristics
444                         if (source_block->getIsUnderground()) {
445                                 sunlight = false;
446                         }
447                 } else {
448                         ContentLightingFlags above_f = ndef->getLightingFlags(above);
449                         if (above.getLight(LIGHTBANK_DAY, above_f) != LIGHT_SUN) {
450                                 // If the node above doesn't have sunlight, this
451                                 // node is in shadow.
452                                 sunlight = false;
453                         }
454                 }
455         }
456         return sunlight;
457 }
458
459 static const LightBank banks[] = { LIGHTBANK_DAY, LIGHTBANK_NIGHT };
460
461 void update_lighting_nodes(Map *map,
462         const std::vector<std::pair<v3s16, MapNode>> &oldnodes,
463         std::map<v3s16, MapBlock*> &modified_blocks)
464 {
465         const NodeDefManager *ndef = map->getNodeDefManager();
466         // For node getter functions
467         bool is_valid_position;
468
469         // Process each light bank separately
470         for (LightBank bank : banks) {
471                 UnlightQueue disappearing_lights(256);
472                 ReLightQueue light_sources(256);
473                 // Nodes that are brighter than the brightest modified node was
474                 // won't change, since they didn't get their light from a
475                 // modified node.
476                 u8 min_safe_light = 0;
477                 for (auto it = oldnodes.cbegin(); it < oldnodes.cend(); ++it) {
478                         u8 old_light = it->second.getLight(bank, ndef->getLightingFlags(it->second));
479                         if (old_light > min_safe_light) {
480                                 min_safe_light = old_light;
481                         }
482                 }
483                 // If only one node changed, even nodes with the same brightness
484                 // didn't get their light from the changed node.
485                 if (oldnodes.size() > 1) {
486                         min_safe_light++;
487                 }
488                 // For each changed node process sunlight and initialize
489                 for (auto it = oldnodes.cbegin(); it < oldnodes.cend(); ++it) {
490                         // Get position and block of the changed node
491                         v3s16 p = it->first;
492                         relative_v3 rel_pos;
493                         mapblock_v3 block_pos;
494                         getNodeBlockPosWithOffset(p, block_pos, rel_pos);
495                         MapBlock *block = map->getBlockNoCreateNoEx(block_pos);
496                         if (block == NULL) {
497                                 continue;
498                         }
499                         // Get the new node
500                         MapNode n = block->getNodeNoCheck(rel_pos);
501
502                         // Light of the old node
503                         u8 old_light = it->second.getLight(bank, ndef->getLightingFlags(it->second));
504
505                         // Add the block of the added node to modified_blocks
506                         modified_blocks[block_pos] = block;
507
508                         // Get new light level of the node
509                         u8 new_light = 0;
510                         ContentLightingFlags f = ndef->getLightingFlags(n);
511                         if (f.light_propagates) {
512                                 if (bank == LIGHTBANK_DAY && f.sunlight_propagates
513                                         && is_sunlight_above(map, p, ndef)) {
514                                         new_light = LIGHT_SUN;
515                                 } else {
516                                         new_light = f.light_source;
517                                         for (const v3s16 &neighbor_dir : neighbor_dirs) {
518                                                 v3s16 p2 = p + neighbor_dir;
519                                                 MapNode n2 = map->getNode(p2, &is_valid_position);
520                                                 if (is_valid_position) {
521                                                         u8 spread = n2.getLight(bank, ndef->getLightingFlags(n2));
522                                                         // If it is sure that the neighbor won't be
523                                                         // unlighted, its light can spread to this node.
524                                                         if (spread > new_light && spread >= min_safe_light) {
525                                                                 new_light = spread - 1;
526                                                         }
527                                                 }
528                                         }
529                                 }
530                         } else {
531                                 // If this is an opaque node, it still can emit light.
532                                 new_light = f.light_source;
533                         }
534
535                         if (new_light > 0) {
536                                 light_sources.push(new_light, rel_pos, block_pos, block, 6);
537                         }
538
539                         if (new_light < old_light) {
540                                 // The node became opaque or doesn't provide as much
541                                 // light as the previous one, so it must be unlighted.
542
543                                 // Add to unlight queue
544                                 n.setLight(bank, 0, f);
545                                 block->setNodeNoCheck(rel_pos, n);
546                                 disappearing_lights.push(old_light, rel_pos, block_pos, block,
547                                         6);
548
549                                 // Remove sunlight, if there was any
550                                 if (bank == LIGHTBANK_DAY && old_light == LIGHT_SUN) {
551                                         for (s16 y = p.Y - 1;; y--) {
552                                                 v3s16 n2pos(p.X, y, p.Z);
553
554                                                 MapNode n2;
555
556                                                 n2 = map->getNode(n2pos, &is_valid_position);
557                                                 if (!is_valid_position)
558                                                         break;
559
560                                                 // If this node doesn't have sunlight, the nodes below
561                                                 // it don't have too.
562                                                 ContentLightingFlags f2 = ndef->getLightingFlags(n2);
563                                                 if (n2.getLight(LIGHTBANK_DAY, f2) != LIGHT_SUN) {
564                                                         break;
565                                                 }
566                                                 // Remove sunlight and add to unlight queue.
567                                                 n2.setLight(LIGHTBANK_DAY, 0, f2);
568                                                 map->setNode(n2pos, n2);
569                                                 relative_v3 rel_pos2;
570                                                 mapblock_v3 block_pos2;
571                                                 getNodeBlockPosWithOffset(n2pos, block_pos2, rel_pos2);
572                                                 MapBlock *block2 = map->getBlockNoCreateNoEx(
573                                                         block_pos2);
574                                                 disappearing_lights.push(LIGHT_SUN, rel_pos2,
575                                                         block_pos2, block2,
576                                                         4 /* The node above caused the change */);
577                                         }
578                                 }
579                         } else if (new_light > old_light) {
580                                 // It is sure that the node provides more light than the previous
581                                 // one, unlighting is not necessary.
582                                 // Propagate sunlight
583                                 if (bank == LIGHTBANK_DAY && new_light == LIGHT_SUN) {
584                                         for (s16 y = p.Y - 1;; y--) {
585                                                 v3s16 n2pos(p.X, y, p.Z);
586
587                                                 MapNode n2;
588
589                                                 n2 = map->getNode(n2pos, &is_valid_position);
590                                                 if (!is_valid_position)
591                                                         break;
592
593                                                 // This should not happen, but if the node has sunlight
594                                                 // then the iteration should stop.
595                                                 ContentLightingFlags f2 = ndef->getLightingFlags(n2);
596                                                 if (n2.getLight(LIGHTBANK_DAY, f2) == LIGHT_SUN) {
597                                                         break;
598                                                 }
599                                                 // If the node terminates sunlight, stop.
600                                                 if (!f2.sunlight_propagates) {
601                                                         break;
602                                                 }
603                                                 relative_v3 rel_pos2;
604                                                 mapblock_v3 block_pos2;
605                                                 getNodeBlockPosWithOffset(n2pos, block_pos2, rel_pos2);
606                                                 MapBlock *block2 = map->getBlockNoCreateNoEx(
607                                                         block_pos2);
608                                                 // Mark node for lighting.
609                                                 light_sources.push(LIGHT_SUN, rel_pos2, block_pos2,
610                                                         block2, 4);
611                                         }
612                                 }
613                         }
614
615                 }
616                 // Remove lights
617                 unspread_light(map, ndef, bank, disappearing_lights, light_sources,
618                         modified_blocks);
619                 // Initialize light values for light spreading.
620                 for (u8 i = 0; i <= LIGHT_SUN; i++) {
621                         const std::vector<ChangingLight> &lights = light_sources.lights[i];
622                         for (std::vector<ChangingLight>::const_iterator it = lights.begin();
623                                         it < lights.end(); ++it) {
624                                 MapNode n = it->block->getNodeNoCheck(it->rel_position);
625                                 n.setLight(bank, i, ndef->getLightingFlags(n));
626                                 it->block->setNodeNoCheck(it->rel_position, n);
627                         }
628                 }
629                 // Spread lights.
630                 spread_light(map, ndef, bank, light_sources, modified_blocks);
631         }
632 }
633
634 /*!
635  * Borders of a map block in relative node coordinates.
636  * Compatible with type 'direction'.
637  */
638 const VoxelArea block_borders[] = {
639         VoxelArea(v3s16(15, 0, 0), v3s16(15, 15, 15)), //X+
640         VoxelArea(v3s16(0, 15, 0), v3s16(15, 15, 15)), //Y+
641         VoxelArea(v3s16(0, 0, 15), v3s16(15, 15, 15)), //Z+
642         VoxelArea(v3s16(0, 0, 0), v3s16(15, 15, 0)),   //Z-
643         VoxelArea(v3s16(0, 0, 0), v3s16(15, 0, 15)),   //Y-
644         VoxelArea(v3s16(0, 0, 0), v3s16(0, 15, 15))    //X-
645 };
646
647 /*!
648  * Returns true if:
649  * -the node has unloaded neighbors
650  * -the node doesn't have light
651  * -the node's light is the same as the maximum of
652  * its light source and its brightest neighbor minus one.
653  * .
654  */
655 bool is_light_locally_correct(Map *map, const NodeDefManager *ndef,
656         LightBank bank, v3s16 pos)
657 {
658         bool is_valid_position;
659         MapNode n = map->getNode(pos, &is_valid_position);
660         ContentLightingFlags f = ndef->getLightingFlags(n);
661         if (!f.has_light) {
662                 return true;
663         }
664         u8 light = n.getLight(bank, f);
665         assert(f.light_source <= LIGHT_MAX);
666         u8 brightest_neighbor = f.light_source + 1;
667         for (const v3s16 &neighbor_dir : neighbor_dirs) {
668                 MapNode n2 = map->getNode(pos + neighbor_dir,
669                         &is_valid_position);
670                 u8 light2 = n2.getLight(bank, ndef->getLightingFlags(n2));
671                 if (brightest_neighbor < light2) {
672                         brightest_neighbor = light2;
673                 }
674         }
675         assert(light <= LIGHT_SUN);
676         return brightest_neighbor == light + 1;
677 }
678
679 void update_block_border_lighting(Map *map, MapBlock *block,
680         std::map<v3s16, MapBlock*> &modified_blocks)
681 {
682         const NodeDefManager *ndef = map->getNodeDefManager();
683         for (LightBank bank : banks) {
684                 // Since invalid light is not common, do not allocate
685                 // memory if not needed.
686                 UnlightQueue disappearing_lights(0);
687                 ReLightQueue light_sources(0);
688                 // Get incorrect lights
689                 for (direction d = 0; d < 6; d++) {
690                         // For each direction
691                         // Get neighbor block
692                         v3s16 otherpos = block->getPos() + neighbor_dirs[d];
693                         MapBlock *other = map->getBlockNoCreateNoEx(otherpos);
694                         if (other == NULL) {
695                                 continue;
696                         }
697                         // Only update if lighting was not completed.
698                         if (block->isLightingComplete(bank, d) &&
699                                         other->isLightingComplete(bank, 5 - d))
700                                 continue;
701                         // Reset flags
702                         block->setLightingComplete(bank, d, true);
703                         other->setLightingComplete(bank, 5 - d, true);
704                         // The two blocks and their connecting surfaces
705                         MapBlock *blocks[] = {block, other};
706                         VoxelArea areas[] = {block_borders[d], block_borders[5 - d]};
707                         // For both blocks
708                         for (u8 blocknum = 0; blocknum < 2; blocknum++) {
709                                 MapBlock *b = blocks[blocknum];
710                                 VoxelArea a = areas[blocknum];
711                                 // For all nodes
712                                 for (s32 x = a.MinEdge.X; x <= a.MaxEdge.X; x++)
713                                 for (s32 z = a.MinEdge.Z; z <= a.MaxEdge.Z; z++)
714                                 for (s32 y = a.MinEdge.Y; y <= a.MaxEdge.Y; y++) {
715                                         MapNode n = b->getNodeNoCheck(x, y, z);
716                                         ContentLightingFlags f = ndef->getLightingFlags(n);
717                                         u8 light = n.getLight(bank, f);
718                                         // Sunlight is fixed
719                                         if (light < LIGHT_SUN) {
720                                                 // Unlight if not correct
721                                                 if (!is_light_locally_correct(map, ndef, bank,
722                                                                 v3s16(x, y, z) + b->getPosRelative())) {
723                                                         // Initialize for unlighting
724                                                         n.setLight(bank, 0, ndef->getLightingFlags(n));
725                                                         b->setNodeNoCheck(x, y, z, n);
726                                                         modified_blocks[b->getPos()]=b;
727                                                         disappearing_lights.push(light,
728                                                                 relative_v3(x, y, z), b->getPos(), b,
729                                                                 6);
730                                                 }
731                                         }
732                                 }
733                         }
734                 }
735                 // Remove lights
736                 unspread_light(map, ndef, bank, disappearing_lights, light_sources,
737                         modified_blocks);
738                 // Initialize light values for light spreading.
739                 for (u8 i = 0; i <= LIGHT_SUN; i++) {
740                         const std::vector<ChangingLight> &lights = light_sources.lights[i];
741                         for (std::vector<ChangingLight>::const_iterator it = lights.begin();
742                                         it < lights.end(); ++it) {
743                                 MapNode n = it->block->getNodeNoCheck(it->rel_position);
744                                 n.setLight(bank, i, ndef->getLightingFlags(n));
745                                 it->block->setNodeNoCheck(it->rel_position, n);
746                         }
747                 }
748                 // Spread lights.
749                 spread_light(map, ndef, bank, light_sources, modified_blocks);
750         }
751 }
752
753 /*!
754  * Resets the lighting of the given VoxelManipulator to
755  * complete darkness and full sunlight.
756  * Operates in one map sector.
757  *
758  * \param offset contains the least x and z node coordinates
759  * of the map sector.
760  * \param light incoming sunlight, light[x][z] is true if there
761  * is sunlight above the voxel manipulator at the given x-z coordinates.
762  * The array's indices are relative node coordinates in the sector.
763  * After the procedure returns, this contains outgoing light at
764  * the bottom of the voxel manipulator.
765  */
766 void fill_with_sunlight(MMVManip *vm, const NodeDefManager *ndef, v2s16 offset,
767         bool light[MAP_BLOCKSIZE][MAP_BLOCKSIZE])
768 {
769         // Distance in array between two nodes on top of each other.
770         s16 ystride = vm->m_area.getExtent().X;
771         // Cache the ignore node.
772         MapNode ignore = MapNode(CONTENT_IGNORE);
773         // For each column of nodes:
774         for (s16 z = 0; z < MAP_BLOCKSIZE; z++)
775         for (s16 x = 0; x < MAP_BLOCKSIZE; x++) {
776                 // Position of the column on the map.
777                 v2s16 realpos = offset + v2s16(x, z);
778                 // Array indices in the voxel manipulator
779                 s32 maxindex = vm->m_area.index(realpos.X, vm->m_area.MaxEdge.Y,
780                         realpos.Y);
781                 s32 minindex = vm->m_area.index(realpos.X, vm->m_area.MinEdge.Y,
782                         realpos.Y);
783                 // True if the current node has sunlight.
784                 bool lig = light[z][x];
785                 // For each node, downwards:
786                 for (s32 i = maxindex; i >= minindex; i -= ystride) {
787                         MapNode *n;
788                         if (vm->m_flags[i] & VOXELFLAG_NO_DATA)
789                                 n = &ignore;
790                         else
791                                 n = &vm->m_data[i];
792                         // Ignore IGNORE nodes, these are not generated yet.
793                         if(n->getContent() == CONTENT_IGNORE)
794                                 continue;
795                         ContentLightingFlags f = ndef->getLightingFlags(*n);
796                         if (lig && !f.sunlight_propagates)
797                                 // Sunlight is stopped.
798                                 lig = false;
799                         // Reset light
800                         n->setLight(LIGHTBANK_DAY, lig ? 15 : 0, f);
801                         n->setLight(LIGHTBANK_NIGHT, 0, f);
802                 }
803                 // Output outgoing light.
804                 light[z][x] = lig;
805         }
806 }
807
808 /*!
809  * Returns incoming sunlight for one map block.
810  * If block above is not found, it is loaded.
811  *
812  * \param pos position of the map block that gets the sunlight.
813  * \param light incoming sunlight, light[z][x] is true if there
814  * is sunlight above the block at the given z-x relative
815  * node coordinates.
816  */
817 void is_sunlight_above_block(Map *map, mapblock_v3 pos,
818         const NodeDefManager *ndef, bool light[MAP_BLOCKSIZE][MAP_BLOCKSIZE])
819 {
820         mapblock_v3 source_block_pos = pos + v3s16(0, 1, 0);
821         // Get or load source block.
822         // It might take a while to load, but correcting incorrect
823         // sunlight may be even slower.
824         MapBlock *source_block = map->emergeBlock(source_block_pos, false);
825         // Trust only generated blocks.
826         if (source_block == NULL || !source_block->isGenerated()) {
827                 // But if there is no block above, then use heuristics
828                 bool sunlight = true;
829                 MapBlock *node_block = map->getBlockNoCreateNoEx(pos);
830                 if (node_block == NULL)
831                         // This should not happen.
832                         sunlight = false;
833                 else
834                         sunlight = !node_block->getIsUnderground();
835                 for (s16 z = 0; z < MAP_BLOCKSIZE; z++)
836                 for (s16 x = 0; x < MAP_BLOCKSIZE; x++)
837                         light[z][x] = sunlight;
838         } else {
839                 // For each column:
840                 for (s16 z = 0; z < MAP_BLOCKSIZE; z++)
841                 for (s16 x = 0; x < MAP_BLOCKSIZE; x++) {
842                         // Get the bottom block.
843                         MapNode above = source_block->getNodeNoCheck(x, 0, z);
844                         ContentLightingFlags above_f = ndef->getLightingFlags(above);
845                         light[z][x] = above.getLight(LIGHTBANK_DAY, above_f) == LIGHT_SUN;
846                 }
847         }
848 }
849
850 /*!
851  * Propagates sunlight down in a given map block.
852  *
853  * \param data contains incoming sunlight and shadow and
854  * the coordinates of the target block.
855  * \param unlight propagated shadow is inserted here
856  * \param relight propagated sunlight is inserted here
857  *
858  * \returns true if the block was modified, false otherwise.
859  */
860 bool propagate_block_sunlight(Map *map, const NodeDefManager *ndef,
861         SunlightPropagationData *data, UnlightQueue *unlight, ReLightQueue *relight)
862 {
863         bool modified = false;
864         // Get the block.
865         MapBlock *block = map->getBlockNoCreateNoEx(data->target_block);
866         if (block == NULL) {
867                 // The work is done if the block does not contain data.
868                 data->data.clear();
869                 return false;
870         }
871         // For each changing column of nodes:
872         size_t index;
873         for (index = 0; index < data->data.size(); index++) {
874                 SunlightPropagationUnit it = data->data[index];
875                 // Relative position of the currently inspected node.
876                 relative_v3 current_pos(it.relative_pos.X, MAP_BLOCKSIZE - 1,
877                         it.relative_pos.Y);
878                 if (it.is_sunlit) {
879                         // Propagate sunlight.
880                         // For each node downwards:
881                         for (; current_pos.Y >= 0; current_pos.Y--) {
882                                 MapNode n = block->getNodeNoCheck(current_pos);
883                                 ContentLightingFlags f = ndef->getLightingFlags(n);
884                                 if (n.getLightRaw(LIGHTBANK_DAY, f) < LIGHT_SUN
885                                                 && f.sunlight_propagates) {
886                                         // This node gets sunlight.
887                                         n.setLight(LIGHTBANK_DAY, LIGHT_SUN, f);
888                                         block->setNodeNoCheck(current_pos, n);
889                                         modified = true;
890                                         relight->push(LIGHT_SUN, current_pos, data->target_block,
891                                                 block, 4);
892                                 } else {
893                                         // Light already valid, propagation stopped.
894                                         break;
895                                 }
896                         }
897                 } else {
898                         // Propagate shadow.
899                         // For each node downwards:
900                         for (; current_pos.Y >= 0; current_pos.Y--) {
901                                 MapNode n = block->getNodeNoCheck(current_pos);
902                                 ContentLightingFlags f = ndef->getLightingFlags(n);
903                                 if (n.getLightRaw(LIGHTBANK_DAY, f) == LIGHT_SUN) {
904                                         // The sunlight is no longer valid.
905                                         n.setLight(LIGHTBANK_DAY, 0, f);
906                                         block->setNodeNoCheck(current_pos, n);
907                                         modified = true;
908                                         unlight->push(LIGHT_SUN, current_pos, data->target_block,
909                                                 block, 4);
910                                 } else {
911                                         // Reached shadow, propagation stopped.
912                                         break;
913                                 }
914                         }
915                 }
916                 if (current_pos.Y >= 0) {
917                         // Propagation stopped, remove from data.
918                         data->data[index] = data->data.back();
919                         data->data.pop_back();
920                         index--;
921                 }
922         }
923         return modified;
924 }
925
926 /*!
927  * Borders of a map block in relative node coordinates.
928  * The areas do not overlap.
929  * Compatible with type 'direction'.
930  */
931 const VoxelArea block_pad[] = {
932         VoxelArea(v3s16(15, 0, 0), v3s16(15, 15, 15)), //X+
933         VoxelArea(v3s16(1, 15, 0), v3s16(14, 15, 15)), //Y+
934         VoxelArea(v3s16(1, 1, 15), v3s16(14, 14, 15)), //Z+
935         VoxelArea(v3s16(1, 1, 0), v3s16(14, 14, 0)),   //Z-
936         VoxelArea(v3s16(1, 0, 0), v3s16(14, 0, 15)),   //Y-
937         VoxelArea(v3s16(0, 0, 0), v3s16(0, 15, 15))    //X-
938 };
939
940 /*!
941  * The common part of bulk light updates - it is always executed.
942  * The procedure takes the nodes that should be unlit, and the
943  * full modified area.
944  *
945  * The procedure handles the correction of all lighting except
946  * direct sunlight spreading.
947  *
948  * \param minblock least coordinates of the changed area in block
949  * coordinates
950  * \param maxblock greatest coordinates of the changed area in block
951  * coordinates
952  * \param unlight the first queue is for day light, the second is for
953  * night light. Contains all nodes on the borders that need to be unlit.
954  * \param relight the first queue is for day light, the second is for
955  * night light. Contains nodes that were not modified, but got sunlight
956  * because the changes.
957  * \param modified_blocks the procedure adds all modified blocks to
958  * this map
959  */
960 void finish_bulk_light_update(Map *map, mapblock_v3 minblock,
961         mapblock_v3 maxblock, UnlightQueue unlight[2], ReLightQueue relight[2],
962         std::map<v3s16, MapBlock*> *modified_blocks)
963 {
964         const NodeDefManager *ndef = map->getNodeDefManager();
965
966         // --- STEP 1: Do unlighting
967
968         for (size_t bank = 0; bank < 2; bank++) {
969                 LightBank b = banks[bank];
970                 unspread_light(map, ndef, b, unlight[bank], relight[bank],
971                         *modified_blocks);
972         }
973
974         // --- STEP 2: Get all newly inserted light sources
975
976         // For each block:
977         v3s16 blockpos;
978         v3s16 relpos;
979         for (blockpos.X = minblock.X; blockpos.X <= maxblock.X; blockpos.X++)
980         for (blockpos.Y = minblock.Y; blockpos.Y <= maxblock.Y; blockpos.Y++)
981         for (blockpos.Z = minblock.Z; blockpos.Z <= maxblock.Z; blockpos.Z++) {
982                 MapBlock *block = map->getBlockNoCreateNoEx(blockpos);
983                 if (!block)
984                         // Skip not existing blocks
985                         continue;
986                 // For each node in the block:
987                 for (relpos.X = 0; relpos.X < MAP_BLOCKSIZE; relpos.X++)
988                 for (relpos.Z = 0; relpos.Z < MAP_BLOCKSIZE; relpos.Z++)
989                 for (relpos.Y = 0; relpos.Y < MAP_BLOCKSIZE; relpos.Y++) {
990                         MapNode node = block->getNodeNoCheck(relpos.X, relpos.Y, relpos.Z);
991                         ContentLightingFlags f = ndef->getLightingFlags(node);
992
993                         // For each light bank
994                         for (size_t b = 0; b < 2; b++) {
995                                 LightBank bank = banks[b];
996                                 u8 light = f.has_light ?
997                                         node.getLight(bank, f):
998                                         f.light_source;
999                                 if (light > 1)
1000                                         relight[b].push(light, relpos, blockpos, block, 6);
1001                         } // end of banks
1002                 } // end of nodes
1003         } // end of blocks
1004
1005         // --- STEP 3: do light spreading
1006
1007         // For each light bank:
1008         for (size_t b = 0; b < 2; b++) {
1009                 LightBank bank = banks[b];
1010                 // Sunlight is already initialized.
1011                 u8 maxlight = (b == 0) ? LIGHT_MAX : LIGHT_SUN;
1012                 // Initialize light values for light spreading.
1013                 for (u8 i = 0; i <= maxlight; i++) {
1014                         const std::vector<ChangingLight> &lights = relight[b].lights[i];
1015                         for (std::vector<ChangingLight>::const_iterator it = lights.begin();
1016                                         it < lights.end(); ++it) {
1017                                 MapNode n = it->block->getNodeNoCheck(it->rel_position);
1018                                 n.setLight(bank, i, ndef->getLightingFlags(n));
1019                                 it->block->setNodeNoCheck(it->rel_position, n);
1020                         }
1021                 }
1022                 // Spread lights.
1023                 spread_light(map, ndef, bank, relight[b], *modified_blocks);
1024         }
1025 }
1026
1027 void blit_back_with_light(Map *map, MMVManip *vm,
1028         std::map<v3s16, MapBlock*> *modified_blocks)
1029 {
1030         const NodeDefManager *ndef = map->getNodeDefManager();
1031         mapblock_v3 minblock = getNodeBlockPos(vm->m_area.MinEdge);
1032         mapblock_v3 maxblock = getNodeBlockPos(vm->m_area.MaxEdge);
1033         // First queue is for day light, second is for night light.
1034         UnlightQueue unlight[] = { UnlightQueue(256), UnlightQueue(256) };
1035         ReLightQueue relight[] = { ReLightQueue(256), ReLightQueue(256) };
1036         // Will hold sunlight data.
1037         bool lights[MAP_BLOCKSIZE][MAP_BLOCKSIZE];
1038         SunlightPropagationData data;
1039
1040         // --- STEP 1: reset everything to sunlight
1041
1042         // For each map block:
1043         for (s16 x = minblock.X; x <= maxblock.X; x++)
1044         for (s16 z = minblock.Z; z <= maxblock.Z; z++) {
1045                 // Extract sunlight above.
1046                 is_sunlight_above_block(map, v3s16(x, maxblock.Y, z), ndef, lights);
1047                 v2s16 offset(x, z);
1048                 offset *= MAP_BLOCKSIZE;
1049                 // Reset the voxel manipulator.
1050                 fill_with_sunlight(vm, ndef, offset, lights);
1051                 // Copy sunlight data
1052                 data.target_block = v3s16(x, minblock.Y - 1, z);
1053                 for (s16 z = 0; z < MAP_BLOCKSIZE; z++)
1054                 for (s16 x = 0; x < MAP_BLOCKSIZE; x++)
1055                         data.data.emplace_back(v2s16(x, z), lights[z][x]);
1056                 // Propagate sunlight and shadow below the voxel manipulator.
1057                 while (!data.data.empty()) {
1058                         if (propagate_block_sunlight(map, ndef, &data, &unlight[0],
1059                                         &relight[0]))
1060                                 (*modified_blocks)[data.target_block] =
1061                                         map->getBlockNoCreateNoEx(data.target_block);
1062                         // Step downwards.
1063                         data.target_block.Y--;
1064                 }
1065         }
1066
1067         // --- STEP 2: Get nodes from borders to unlight
1068         v3s16 blockpos;
1069         v3s16 relpos;
1070
1071         // In case there are unloaded holes in the voxel manipulator
1072         // unlight each block.
1073         // For each block:
1074         for (blockpos.X = minblock.X; blockpos.X <= maxblock.X; blockpos.X++)
1075         for (blockpos.Y = minblock.Y; blockpos.Y <= maxblock.Y; blockpos.Y++)
1076         for (blockpos.Z = minblock.Z; blockpos.Z <= maxblock.Z; blockpos.Z++) {
1077                 MapBlock *block = map->getBlockNoCreateNoEx(blockpos);
1078                 if (!block)
1079                         // Skip not existing blocks.
1080                         continue;
1081                 v3s16 offset = block->getPosRelative();
1082                 // For each border of the block:
1083                 for (const VoxelArea &a : block_pad) {
1084                         // For each node of the border:
1085                         for (relpos.X = a.MinEdge.X; relpos.X <= a.MaxEdge.X; relpos.X++)
1086                         for (relpos.Z = a.MinEdge.Z; relpos.Z <= a.MaxEdge.Z; relpos.Z++)
1087                         for (relpos.Y = a.MinEdge.Y; relpos.Y <= a.MaxEdge.Y; relpos.Y++) {
1088
1089                                 // Get old and new node
1090                                 MapNode oldnode = block->getNodeNoCheck(relpos);
1091                                 ContentLightingFlags oldf = ndef->getLightingFlags(oldnode);
1092                                 MapNode newnode = vm->getNodeNoExNoEmerge(relpos + offset);
1093                                 ContentLightingFlags newf = ndef->getLightingFlags(newnode);
1094
1095                                 // For each light bank
1096                                 for (size_t b = 0; b < 2; b++) {
1097                                         LightBank bank = banks[b];
1098                                         u8 oldlight = oldf.has_light ?
1099                                                 oldnode.getLight(bank, oldf):
1100                                                 LIGHT_SUN; // no light information, force unlighting
1101                                         u8 newlight = newf.has_light ?
1102                                                 newnode.getLight(bank, newf):
1103                                                 newf.light_source;
1104                                         // If the new node is dimmer, unlight.
1105                                         if (oldlight > newlight) {
1106                                                 unlight[b].push(
1107                                                         oldlight, relpos, blockpos, block, 6);
1108                                         }
1109                                 } // end of banks
1110                         } // end of nodes
1111                 } // end of borders
1112         } // end of blocks
1113
1114         // --- STEP 3: All information extracted, overwrite
1115
1116         vm->blitBackAll(modified_blocks, true);
1117
1118         // --- STEP 4: Finish light update
1119
1120         finish_bulk_light_update(map, minblock, maxblock, unlight, relight,
1121                 modified_blocks);
1122 }
1123
1124 /*!
1125  * Resets the lighting of the given map block to
1126  * complete darkness and full sunlight.
1127  *
1128  * \param light incoming sunlight, light[x][z] is true if there
1129  * is sunlight above the map block at the given x-z coordinates.
1130  * The array's indices are relative node coordinates in the block.
1131  * After the procedure returns, this contains outgoing light at
1132  * the bottom of the map block.
1133  */
1134 void fill_with_sunlight(MapBlock *block, const NodeDefManager *ndef,
1135         bool light[MAP_BLOCKSIZE][MAP_BLOCKSIZE])
1136 {
1137         // For each column of nodes:
1138         for (s16 z = 0; z < MAP_BLOCKSIZE; z++)
1139         for (s16 x = 0; x < MAP_BLOCKSIZE; x++) {
1140                 // True if the current node has sunlight.
1141                 bool lig = light[z][x];
1142                 // For each node, downwards:
1143                 for (s16 y = MAP_BLOCKSIZE - 1; y >= 0; y--) {
1144                         MapNode n = block->getNodeNoCheck(x, y, z);
1145                         // Ignore IGNORE nodes, these are not generated yet.
1146                         if (n.getContent() == CONTENT_IGNORE)
1147                                 continue;
1148                         ContentLightingFlags f = ndef->getLightingFlags(n);
1149                         if (lig && !f.sunlight_propagates) {
1150                                 // Sunlight is stopped.
1151                                 lig = false;
1152                         }
1153                         // Reset light
1154                         n.setLight(LIGHTBANK_DAY, lig ? 15 : 0, f);
1155                         n.setLight(LIGHTBANK_NIGHT, 0, f);
1156                         block->setNodeNoCheck(x, y, z, n);
1157                 }
1158                 // Output outgoing light.
1159                 light[z][x] = lig;
1160         }
1161 }
1162
1163 void repair_block_light(Map *map, MapBlock *block,
1164         std::map<v3s16, MapBlock*> *modified_blocks)
1165 {
1166         if (!block)
1167                 return;
1168         const NodeDefManager *ndef = map->getNodeDefManager();
1169         // First queue is for day light, second is for night light.
1170         UnlightQueue unlight[] = { UnlightQueue(256), UnlightQueue(256) };
1171         ReLightQueue relight[] = { ReLightQueue(256), ReLightQueue(256) };
1172         // Will hold sunlight data.
1173         bool lights[MAP_BLOCKSIZE][MAP_BLOCKSIZE];
1174         SunlightPropagationData data;
1175
1176         // --- STEP 1: reset everything to sunlight
1177
1178         mapblock_v3 blockpos = block->getPos();
1179         (*modified_blocks)[blockpos] = block;
1180         // For each map block:
1181         // Extract sunlight above.
1182         is_sunlight_above_block(map, blockpos, ndef, lights);
1183         // Reset the voxel manipulator.
1184         fill_with_sunlight(block, ndef, lights);
1185         // Copy sunlight data
1186         data.target_block = v3s16(blockpos.X, blockpos.Y - 1, blockpos.Z);
1187         for (s16 z = 0; z < MAP_BLOCKSIZE; z++)
1188         for (s16 x = 0; x < MAP_BLOCKSIZE; x++) {
1189                 data.data.emplace_back(v2s16(x, z), lights[z][x]);
1190         }
1191         // Propagate sunlight and shadow below the voxel manipulator.
1192         while (!data.data.empty()) {
1193                 if (propagate_block_sunlight(map, ndef, &data, &unlight[0],
1194                                 &relight[0]))
1195                         (*modified_blocks)[data.target_block] =
1196                                 map->getBlockNoCreateNoEx(data.target_block);
1197                 // Step downwards.
1198                 data.target_block.Y--;
1199         }
1200
1201         // --- STEP 2: Get nodes from borders to unlight
1202
1203         // For each border of the block:
1204         for (const VoxelArea &a : block_pad) {
1205                 v3s16 relpos;
1206                 // For each node of the border:
1207                 for (relpos.X = a.MinEdge.X; relpos.X <= a.MaxEdge.X; relpos.X++)
1208                 for (relpos.Z = a.MinEdge.Z; relpos.Z <= a.MaxEdge.Z; relpos.Z++)
1209                 for (relpos.Y = a.MinEdge.Y; relpos.Y <= a.MaxEdge.Y; relpos.Y++) {
1210
1211                         // Get node
1212                         MapNode node = block->getNodeNoCheck(relpos);
1213                         ContentLightingFlags f = ndef->getLightingFlags(node);
1214                         // For each light bank
1215                         for (size_t b = 0; b < 2; b++) {
1216                                 LightBank bank = banks[b];
1217                                 u8 light = f.has_light ?
1218                                         node.getLight(bank, f):
1219                                         f.light_source;
1220                                 // If the new node is dimmer than sunlight, unlight.
1221                                 // (if it has maximal light, it is pointless to remove
1222                                 // surrounding light, as it can only become brighter)
1223                                 if (LIGHT_SUN > light) {
1224                                         unlight[b].push(
1225                                                 LIGHT_SUN, relpos, blockpos, block, 6);
1226                                 }
1227                         } // end of banks
1228                 } // end of nodes
1229         } // end of borders
1230
1231         // STEP 3: Remove and spread light
1232
1233         finish_bulk_light_update(map, blockpos, blockpos, unlight, relight,
1234                 modified_blocks);
1235 }
1236
1237 VoxelLineIterator::VoxelLineIterator(const v3f &start_position, const v3f &line_vector) :
1238         m_start_position(start_position),
1239         m_line_vector(line_vector)
1240 {
1241         m_current_node_pos = floatToInt(m_start_position, 1);
1242         m_start_node_pos = m_current_node_pos;
1243         m_last_index = getIndex(floatToInt(start_position + line_vector, 1));
1244
1245         if (m_line_vector.X > 0) {
1246                 m_next_intersection_multi.X = (floorf(m_start_position.X - 0.5) + 1.5
1247                         - m_start_position.X) / m_line_vector.X;
1248                 m_intersection_multi_inc.X = 1 / m_line_vector.X;
1249         } else if (m_line_vector.X < 0) {
1250                 m_next_intersection_multi.X = (floorf(m_start_position.X - 0.5)
1251                         - m_start_position.X + 0.5) / m_line_vector.X;
1252                 m_intersection_multi_inc.X = -1 / m_line_vector.X;
1253                 m_step_directions.X = -1;
1254         }
1255
1256         if (m_line_vector.Y > 0) {
1257                 m_next_intersection_multi.Y = (floorf(m_start_position.Y - 0.5) + 1.5
1258                         - m_start_position.Y) / m_line_vector.Y;
1259                 m_intersection_multi_inc.Y = 1 / m_line_vector.Y;
1260         } else if (m_line_vector.Y < 0) {
1261                 m_next_intersection_multi.Y = (floorf(m_start_position.Y - 0.5)
1262                         - m_start_position.Y + 0.5) / m_line_vector.Y;
1263                 m_intersection_multi_inc.Y = -1 / m_line_vector.Y;
1264                 m_step_directions.Y = -1;
1265         }
1266
1267         if (m_line_vector.Z > 0) {
1268                 m_next_intersection_multi.Z = (floorf(m_start_position.Z - 0.5) + 1.5
1269                         - m_start_position.Z) / m_line_vector.Z;
1270                 m_intersection_multi_inc.Z = 1 / m_line_vector.Z;
1271         } else if (m_line_vector.Z < 0) {
1272                 m_next_intersection_multi.Z = (floorf(m_start_position.Z - 0.5)
1273                         - m_start_position.Z + 0.5) / m_line_vector.Z;
1274                 m_intersection_multi_inc.Z = -1 / m_line_vector.Z;
1275                 m_step_directions.Z = -1;
1276         }
1277 }
1278
1279 void VoxelLineIterator::next()
1280 {
1281         m_current_index++;
1282         if ((m_next_intersection_multi.X < m_next_intersection_multi.Y)
1283                         && (m_next_intersection_multi.X < m_next_intersection_multi.Z)) {
1284                 m_next_intersection_multi.X += m_intersection_multi_inc.X;
1285                 m_current_node_pos.X += m_step_directions.X;
1286         } else if ((m_next_intersection_multi.Y < m_next_intersection_multi.Z)) {
1287                 m_next_intersection_multi.Y += m_intersection_multi_inc.Y;
1288                 m_current_node_pos.Y += m_step_directions.Y;
1289         } else {
1290                 m_next_intersection_multi.Z += m_intersection_multi_inc.Z;
1291                 m_current_node_pos.Z += m_step_directions.Z;
1292         }
1293 }
1294
1295 s16 VoxelLineIterator::getIndex(v3s16 voxel){
1296         return
1297                 abs(voxel.X - m_start_node_pos.X) +
1298                 abs(voxel.Y - m_start_node_pos.Y) +
1299                 abs(voxel.Z - m_start_node_pos.Z);
1300 }
1301
1302 } // namespace voxalgo
1303