3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
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.
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.
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.
20 #include "voxelalgorithms.h"
37 * Two directions are opposite only if their sum is 5.
41 * Relative node position.
42 * This represents a node's position in its map block.
43 * All coordinates must be between 0 and 15.
45 typedef v3s16 relative_v3;
47 * Position of a map block (block coordinates).
48 * One block_pos unit is as long as 16 node position units.
50 typedef v3s16 mapblock_v3;
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;
61 * Direction from the node that caused this node's changing
64 direction source_direction = 6;
66 ChangingLight() = default;
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),
73 source_direction(source_dir)
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.
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.
89 * Creates a LightQueue.
90 * \param reserve for each light level that many slots are reserved.
92 LightQueue(size_t reserve)
94 max_light = LIGHT_SUN;
95 for (u8 i = 0; i <= LIGHT_SUN; i++) {
96 lights[i].reserve(reserve);
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
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.
109 bool next(u8 &light, ChangingLight &data)
111 while (lights[max_light].empty()) {
112 if (max_light == 0) {
118 data = lights[max_light].back();
119 lights[max_light].pop_back();
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
128 inline void push(u8 light, relative_v3 rel_pos,
129 mapblock_v3 block_pos, MapBlock *block,
130 direction source_dir)
132 assert(light <= LIGHT_SUN);
133 lights[light].emplace_back(rel_pos, block_pos, block, source_dir);
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.
144 typedef LightQueue UnlightQueue;
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
154 typedef LightQueue ReLightQueue;
157 * neighbor_dirs[i] points towards
159 * See the definition of the type "direction"
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
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
177 bool step_rel_block_pos(direction dir, relative_v3 &rel_pos,
178 mapblock_v3 &block_pos)
182 if (rel_pos.X < MAP_BLOCKSIZE - 1) {
191 if (rel_pos.Y < MAP_BLOCKSIZE - 1) {
200 if (rel_pos.Z < MAP_BLOCKSIZE - 1) {
212 rel_pos.Z = MAP_BLOCKSIZE - 1;
221 rel_pos.Y = MAP_BLOCKSIZE - 1;
230 rel_pos.X = MAP_BLOCKSIZE - 1;
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.
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
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)
253 // Stores data popped from from_nodes
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
264 // There is no brightest neighbor
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++) {
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) {
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);
291 neighbor_block = current.block;
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;
315 // The neighbor can light up this node.
316 if (neighbor_light < neighbor_f.light_source) {
317 neighbor_light = neighbor_f.light_source;
319 if (brightest_neighbor_light < neighbor_light) {
320 brightest_neighbor_light = neighbor_light;
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*/);
338 * Spreads light from the specified starting nodes.
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).
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
349 void spread_light(Map *map, const NodeDefManager *nodemgr, LightBank bank,
350 LightQueue &light_sources,
351 std::map<v3s16, MapBlock*> &modified_blocks)
353 // The light the current node can provide to its neighbors.
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)) {
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) {
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);
378 neighbor_block = current.block;
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;
402 struct SunlightPropagationUnit{
406 SunlightPropagationUnit(v2s16 relpos, bool sunlit):
407 relative_pos(relpos),
412 struct SunlightPropagationData{
413 std::vector<SunlightPropagationUnit> data;
418 * Returns true if the node gets sunlight from the
421 * \param pos position of the node.
423 bool is_sunlight_above(Map *map, v3s16 pos, const NodeDefManager *ndef)
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,
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) {
438 sunlight = !node_block->getIsUnderground();
441 MapNode above = source_block->getNodeNoCheck(source_rel_pos);
442 if (above.getContent() == CONTENT_IGNORE) {
444 if (source_block->getIsUnderground()) {
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.
459 static const LightBank banks[] = { LIGHTBANK_DAY, LIGHTBANK_NIGHT };
461 void update_lighting_nodes(Map *map,
462 const std::vector<std::pair<v3s16, MapNode>> &oldnodes,
463 std::map<v3s16, MapBlock*> &modified_blocks)
465 const NodeDefManager *ndef = map->getNodeDefManager();
466 // For node getter functions
467 bool is_valid_position;
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
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;
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) {
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
493 mapblock_v3 block_pos;
494 getNodeBlockPosWithOffset(p, block_pos, rel_pos);
495 MapBlock *block = map->getBlockNoCreateNoEx(block_pos);
500 MapNode n = block->getNodeNoCheck(rel_pos);
502 // Light of the old node
503 u8 old_light = it->second.getLight(bank, ndef->getLightingFlags(it->second));
505 // Add the block of the added node to modified_blocks
506 modified_blocks[block_pos] = block;
508 // Get new light level of the node
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;
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;
531 // If this is an opaque node, it still can emit light.
532 new_light = f.light_source;
536 light_sources.push(new_light, rel_pos, block_pos, block, 6);
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.
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,
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);
556 n2 = map->getNode(n2pos, &is_valid_position);
557 if (!is_valid_position)
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) {
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(
574 disappearing_lights.push(LIGHT_SUN, rel_pos2,
576 4 /* The node above caused the change */);
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);
589 n2 = map->getNode(n2pos, &is_valid_position);
590 if (!is_valid_position)
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) {
599 // If the node terminates sunlight, stop.
600 if (!f2.sunlight_propagates) {
603 relative_v3 rel_pos2;
604 mapblock_v3 block_pos2;
605 getNodeBlockPosWithOffset(n2pos, block_pos2, rel_pos2);
606 MapBlock *block2 = map->getBlockNoCreateNoEx(
608 // Mark node for lighting.
609 light_sources.push(LIGHT_SUN, rel_pos2, block_pos2,
617 unspread_light(map, ndef, bank, disappearing_lights, light_sources,
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);
630 spread_light(map, ndef, bank, light_sources, modified_blocks);
635 * Borders of a map block in relative node coordinates.
636 * Compatible with type 'direction'.
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-
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.
655 bool is_light_locally_correct(Map *map, const NodeDefManager *ndef,
656 LightBank bank, v3s16 pos)
658 bool is_valid_position;
659 MapNode n = map->getNode(pos, &is_valid_position);
660 ContentLightingFlags f = ndef->getLightingFlags(n);
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,
670 u8 light2 = n2.getLight(bank, ndef->getLightingFlags(n2));
671 if (brightest_neighbor < light2) {
672 brightest_neighbor = light2;
675 assert(light <= LIGHT_SUN);
676 return brightest_neighbor == light + 1;
679 void update_block_border_lighting(Map *map, MapBlock *block,
680 std::map<v3s16, MapBlock*> &modified_blocks)
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);
697 // Only update if lighting was not completed.
698 if (block->isLightingComplete(bank, d) &&
699 other->isLightingComplete(bank, 5 - d))
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]};
708 for (u8 blocknum = 0; blocknum < 2; blocknum++) {
709 MapBlock *b = blocks[blocknum];
710 VoxelArea a = areas[blocknum];
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);
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,
736 unspread_light(map, ndef, bank, disappearing_lights, light_sources,
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);
749 spread_light(map, ndef, bank, light_sources, modified_blocks);
754 * Resets the lighting of the given VoxelManipulator to
755 * complete darkness and full sunlight.
756 * Operates in one map sector.
758 * \param offset contains the least x and z node coordinates
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.
766 void fill_with_sunlight(MMVManip *vm, const NodeDefManager *ndef, v2s16 offset,
767 bool light[MAP_BLOCKSIZE][MAP_BLOCKSIZE])
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,
781 s32 minindex = vm->m_area.index(realpos.X, vm->m_area.MinEdge.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) {
788 if (vm->m_flags[i] & VOXELFLAG_NO_DATA)
792 // Ignore IGNORE nodes, these are not generated yet.
793 if(n->getContent() == CONTENT_IGNORE)
795 ContentLightingFlags f = ndef->getLightingFlags(*n);
796 if (lig && !f.sunlight_propagates)
797 // Sunlight is stopped.
800 n->setLight(LIGHTBANK_DAY, lig ? 15 : 0, f);
801 n->setLight(LIGHTBANK_NIGHT, 0, f);
803 // Output outgoing light.
809 * Returns incoming sunlight for one map block.
810 * If block above is not found, it is loaded.
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
817 void is_sunlight_above_block(Map *map, mapblock_v3 pos,
818 const NodeDefManager *ndef, bool light[MAP_BLOCKSIZE][MAP_BLOCKSIZE])
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.
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;
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;
851 * Propagates sunlight down in a given map block.
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
858 * \returns true if the block was modified, false otherwise.
860 bool propagate_block_sunlight(Map *map, const NodeDefManager *ndef,
861 SunlightPropagationData *data, UnlightQueue *unlight, ReLightQueue *relight)
863 bool modified = false;
865 MapBlock *block = map->getBlockNoCreateNoEx(data->target_block);
867 // The work is done if the block does not contain data.
871 // For each changing column of nodes:
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,
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);
890 relight->push(LIGHT_SUN, current_pos, data->target_block,
893 // Light already valid, propagation stopped.
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);
908 unlight->push(LIGHT_SUN, current_pos, data->target_block,
911 // Reached shadow, propagation stopped.
916 if (current_pos.Y >= 0) {
917 // Propagation stopped, remove from data.
918 data->data[index] = data->data.back();
919 data->data.pop_back();
927 * Borders of a map block in relative node coordinates.
928 * The areas do not overlap.
929 * Compatible with type 'direction'.
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-
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.
945 * The procedure handles the correction of all lighting except
946 * direct sunlight spreading.
948 * \param minblock least coordinates of the changed area in block
950 * \param maxblock greatest coordinates of the changed area in block
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
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)
964 const NodeDefManager *ndef = map->getNodeDefManager();
966 // --- STEP 1: Do unlighting
968 for (size_t bank = 0; bank < 2; bank++) {
969 LightBank b = banks[bank];
970 unspread_light(map, ndef, b, unlight[bank], relight[bank],
974 // --- STEP 2: Get all newly inserted light sources
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);
984 // Skip not existing blocks
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);
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):
1000 relight[b].push(light, relpos, blockpos, block, 6);
1005 // --- STEP 3: do light spreading
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);
1023 spread_light(map, ndef, bank, relight[b], *modified_blocks);
1027 void blit_back_with_light(Map *map, MMVManip *vm,
1028 std::map<v3s16, MapBlock*> *modified_blocks)
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;
1040 // --- STEP 1: reset everything to sunlight
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);
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],
1060 (*modified_blocks)[data.target_block] =
1061 map->getBlockNoCreateNoEx(data.target_block);
1063 data.target_block.Y--;
1067 // --- STEP 2: Get nodes from borders to unlight
1071 // In case there are unloaded holes in the voxel manipulator
1072 // unlight 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);
1079 // Skip not existing blocks.
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++) {
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);
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):
1104 // If the new node is dimmer, unlight.
1105 if (oldlight > newlight) {
1107 oldlight, relpos, blockpos, block, 6);
1114 // --- STEP 3: All information extracted, overwrite
1116 vm->blitBackAll(modified_blocks, true);
1118 // --- STEP 4: Finish light update
1120 finish_bulk_light_update(map, minblock, maxblock, unlight, relight,
1125 * Resets the lighting of the given map block to
1126 * complete darkness and full sunlight.
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.
1134 void fill_with_sunlight(MapBlock *block, const NodeDefManager *ndef,
1135 bool light[MAP_BLOCKSIZE][MAP_BLOCKSIZE])
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)
1148 ContentLightingFlags f = ndef->getLightingFlags(n);
1149 if (lig && !f.sunlight_propagates) {
1150 // Sunlight is stopped.
1154 n.setLight(LIGHTBANK_DAY, lig ? 15 : 0, f);
1155 n.setLight(LIGHTBANK_NIGHT, 0, f);
1156 block->setNodeNoCheck(x, y, z, n);
1158 // Output outgoing light.
1163 void repair_block_light(Map *map, MapBlock *block,
1164 std::map<v3s16, MapBlock*> *modified_blocks)
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;
1176 // --- STEP 1: reset everything to sunlight
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]);
1191 // Propagate sunlight and shadow below the voxel manipulator.
1192 while (!data.data.empty()) {
1193 if (propagate_block_sunlight(map, ndef, &data, &unlight[0],
1195 (*modified_blocks)[data.target_block] =
1196 map->getBlockNoCreateNoEx(data.target_block);
1198 data.target_block.Y--;
1201 // --- STEP 2: Get nodes from borders to unlight
1203 // For each border of the block:
1204 for (const VoxelArea &a : block_pad) {
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++) {
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):
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) {
1225 LIGHT_SUN, relpos, blockpos, block, 6);
1231 // STEP 3: Remove and spread light
1233 finish_bulk_light_update(map, blockpos, blockpos, unlight, relight,
1237 VoxelLineIterator::VoxelLineIterator(const v3f &start_position, const v3f &line_vector) :
1238 m_start_position(start_position),
1239 m_line_vector(line_vector)
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));
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;
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;
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;
1279 void VoxelLineIterator::next()
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;
1290 m_next_intersection_multi.Z += m_intersection_multi_inc.Z;
1291 m_current_node_pos.Z += m_step_directions.Z;
1295 s16 VoxelLineIterator::getIndex(v3s16 voxel){
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);
1302 } // namespace voxalgo