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"
28 void setLight(VoxelManipulator &v, VoxelArea a, u8 light,
29 INodeDefManager *ndef)
31 for(s32 x=a.MinEdge.X; x<=a.MaxEdge.X; x++)
32 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
33 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
36 MapNode &n = v.getNodeRefUnsafe(p);
37 n.setLight(LIGHTBANK_DAY, light, ndef);
38 n.setLight(LIGHTBANK_NIGHT, light, ndef);
42 void clearLightAndCollectSources(VoxelManipulator &v, VoxelArea a,
43 enum LightBank bank, INodeDefManager *ndef,
44 std::set<v3s16> & light_sources,
45 std::map<v3s16, u8> & unlight_from)
47 // The full area we shall touch
48 VoxelArea required_a = a;
49 required_a.pad(v3s16(0,0,0));
50 // Make sure we have access to it
53 for(s32 x=a.MinEdge.X; x<=a.MaxEdge.X; x++)
54 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
55 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
58 MapNode &n = v.getNodeRefUnsafe(p);
59 u8 oldlight = n.getLight(bank, ndef);
60 n.setLight(bank, 0, ndef);
62 // If node sources light, add to list
63 u8 source = ndef->get(n).light_source;
65 light_sources.insert(p);
67 // Collect borders for unlighting
68 if((x==a.MinEdge.X || x == a.MaxEdge.X
69 || y==a.MinEdge.Y || y == a.MaxEdge.Y
70 || z==a.MinEdge.Z || z == a.MaxEdge.Z)
73 unlight_from[p] = oldlight;
78 SunlightPropagateResult propagateSunlight(VoxelManipulator &v, VoxelArea a,
79 bool inexistent_top_provides_sunlight,
80 std::set<v3s16> & light_sources,
81 INodeDefManager *ndef)
84 bool bottom_sunlight_valid = true;
86 // The full area we shall touch extends one extra at top and bottom
87 VoxelArea required_a = a;
88 required_a.pad(v3s16(0,1,0));
89 // Make sure we have access to it
92 s16 max_y = a.MaxEdge.Y;
93 s16 min_y = a.MinEdge.Y;
95 for(s32 x=a.MinEdge.X; x<=a.MaxEdge.X; x++)
96 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
98 v3s16 p_overtop(x, max_y+1, z);
99 bool overtop_has_sunlight = false;
100 // If overtop node does not exist, trust heuristics
101 if(!v.exists(p_overtop))
102 overtop_has_sunlight = inexistent_top_provides_sunlight;
103 else if(v.getNodeRefUnsafe(p_overtop).getContent() == CONTENT_IGNORE)
104 overtop_has_sunlight = inexistent_top_provides_sunlight;
105 // Otherwise refer to it's light value
107 overtop_has_sunlight = (v.getNodeRefUnsafe(p_overtop).getLight(
108 LIGHTBANK_DAY, ndef) == LIGHT_SUN);
110 // Copy overtop's sunlight all over the place
111 u8 incoming_light = overtop_has_sunlight ? LIGHT_SUN : 0;
112 for(s32 y=max_y; y>=min_y; y--)
115 MapNode &n = v.getNodeRefUnsafe(p);
116 if(incoming_light == 0){
118 } else if(incoming_light == LIGHT_SUN &&
119 ndef->get(n).sunlight_propagates){
121 } else if(ndef->get(n).sunlight_propagates == false){
124 incoming_light = diminish_light(incoming_light);
126 u8 old_light = n.getLight(LIGHTBANK_DAY, ndef);
128 if(incoming_light > old_light)
129 n.setLight(LIGHTBANK_DAY, incoming_light, ndef);
131 if(diminish_light(incoming_light) != 0)
132 light_sources.insert(p);
135 // Check validity of sunlight at top of block below if it
136 // hasn't already been proven invalid
137 if(bottom_sunlight_valid)
139 bool sunlight_should_continue_down = (incoming_light == LIGHT_SUN);
140 v3s16 p_overbottom(x, min_y-1, z);
141 if(!v.exists(p_overbottom) ||
142 v.getNodeRefUnsafe(p_overbottom
143 ).getContent() == CONTENT_IGNORE){
144 // Is not known, cannot compare
146 bool overbottom_has_sunlight = (v.getNodeRefUnsafe(p_overbottom
147 ).getLight(LIGHTBANK_DAY, ndef) == LIGHT_SUN);
148 if(sunlight_should_continue_down != overbottom_has_sunlight){
149 bottom_sunlight_valid = false;
155 return SunlightPropagateResult(bottom_sunlight_valid);
167 * Two directions are opposite only if their sum is 5.
169 typedef u8 direction;
171 * Relative node position.
172 * This represents a node's position in its map block.
173 * All coordinates must be between 0 and 15.
175 typedef v3s16 relative_v3;
177 * Position of a map block (block coordinates).
178 * One block_pos unit is as long as 16 node position units.
180 typedef v3s16 mapblock_v3;
182 //! Contains information about a node whose light is about to change.
183 struct ChangingLight {
184 //! Relative position of the node in its map block.
185 relative_v3 rel_position;
186 //! Position of the node's block.
187 mapblock_v3 block_position;
188 //! Pointer to the node's block.
191 * Direction from the node that caused this node's changing
194 direction source_direction;
203 ChangingLight(relative_v3 rel_pos, mapblock_v3 block_pos,
204 MapBlock *b, direction source_dir) :
205 rel_position(rel_pos),
206 block_position(block_pos),
208 source_direction(source_dir)
213 * A fast, priority queue-like container to contain ChangingLights.
214 * The ChangingLights are ordered by the given light levels.
215 * The brightest ChangingLight is returned first.
218 //! For each light level there is a vector.
219 std::vector<ChangingLight> lights[LIGHT_SUN + 1];
220 //! Light of the brightest ChangingLight in the queue.
224 * Creates a LightQueue.
225 * \param reserve for each light level that many slots are reserved.
227 LightQueue(size_t reserve)
229 max_light = LIGHT_SUN;
230 for (u8 i = 0; i <= LIGHT_SUN; i++) {
231 lights[i].reserve(reserve);
236 * Returns the next brightest ChangingLight and
237 * removes it from the queue.
238 * If there were no elements in the queue, the given parameters
240 * \param light light level of the popped ChangingLight
241 * \param data the ChangingLight that was popped
242 * \returns true if there was a ChangingLight in the queue.
244 bool next(u8 &light, ChangingLight &data)
246 while (lights[max_light].empty()) {
247 if (max_light == 0) {
253 data = lights[max_light].back();
254 lights[max_light].pop_back();
259 * Adds an element to the queue.
260 * The parameters are the same as in ChangingLight's constructor.
261 * \param light light level of the ChangingLight
263 inline void push(u8 light, const relative_v3 &rel_pos,
264 const mapblock_v3 &block_pos, MapBlock *block,
265 direction source_dir)
267 assert(light <= LIGHT_SUN);
268 lights[light].push_back(
269 ChangingLight(rel_pos, block_pos, block, source_dir));
274 * This type of light queue is for unlighting.
275 * A node can be pushed in it only if its raw light is zero.
276 * This prevents pushing nodes twice into this queue.
277 * The light of the pushed ChangingLight must be the
278 * light of the node before unlighting it.
280 typedef LightQueue UnlightQueue;
282 * This type of light queue is for spreading lights.
283 * While spreading lights, all the nodes in it must
284 * have the same light as the light level the ChangingLights
285 * were pushed into this queue with. This prevents unnecessary
286 * re-pushing of the nodes into the queue.
287 * If a node doesn't let light trough but emits light, it can be added
290 typedef LightQueue ReLightQueue;
293 * neighbor_dirs[i] points towards
295 * See the definition of the type "direction"
297 const static v3s16 neighbor_dirs[6] = {
298 v3s16(1, 0, 0), // right
299 v3s16(0, 1, 0), // top
300 v3s16(0, 0, 1), // back
301 v3s16(0, 0, -1), // front
302 v3s16(0, -1, 0), // bottom
303 v3s16(-1, 0, 0), // left
307 * Transforms the given map block offset by one node towards
308 * the specified direction.
309 * \param dir the direction of the transformation
310 * \param rel_pos the node's relative position in its map block
311 * \param block_pos position of the node's block
313 bool step_rel_block_pos(direction dir, relative_v3 &rel_pos,
314 mapblock_v3 &block_pos)
318 if (rel_pos.X < MAP_BLOCKSIZE - 1) {
327 if (rel_pos.Y < MAP_BLOCKSIZE - 1) {
336 if (rel_pos.Z < MAP_BLOCKSIZE - 1) {
348 rel_pos.Z = MAP_BLOCKSIZE - 1;
357 rel_pos.Y = MAP_BLOCKSIZE - 1;
366 rel_pos.X = MAP_BLOCKSIZE - 1;
376 * Removes all light that is potentially emitted by the specified
377 * light sources. These nodes will have zero light.
378 * Returns all nodes whose light became zero but should be re-lighted.
380 * \param bank the light bank in which the procedure operates
381 * \param from_nodes nodes whose light is removed
382 * \param light_sources nodes that should be re-lighted
383 * \param modified_blocks output, all modified map blocks are added to this
385 void unspread_light(Map *map, INodeDefManager *nodemgr, LightBank bank,
386 UnlightQueue &from_nodes, ReLightQueue &light_sources,
387 std::map<v3s16, MapBlock*> &modified_blocks)
389 // Stores data popped from from_nodes
391 ChangingLight current;
392 // Data of the current neighbor
393 mapblock_v3 neighbor_block_pos;
394 relative_v3 neighbor_rel_pos;
396 bool is_valid_position;
397 // Direction of the brightest neighbor of the node
398 direction source_dir;
399 while (from_nodes.next(current_light, current)) {
400 // For all nodes that need unlighting
402 // There is no brightest neighbor
405 const MapNode &node = current.block->getNodeNoCheck(
406 current.rel_position, &is_valid_position);
407 const ContentFeatures &f = nodemgr->get(node);
408 // If the node emits light, it behaves like it had a
409 // brighter neighbor.
410 u8 brightest_neighbor_light = f.light_source + 1;
411 for (direction i = 0; i < 6; i++) {
414 // The node that changed this node has already zero light
415 // and it can't give light to this node
416 if (current.source_direction + i == 5) {
419 // Get the neighbor's position and block
420 neighbor_rel_pos = current.rel_position;
421 neighbor_block_pos = current.block_position;
422 MapBlock *neighbor_block;
423 if (step_rel_block_pos(i, neighbor_rel_pos, neighbor_block_pos)) {
424 neighbor_block = map->getBlockNoCreateNoEx(neighbor_block_pos);
425 if (neighbor_block == NULL) {
426 current.block->setLightingComplete(bank, i, false);
430 neighbor_block = current.block;
432 // Get the neighbor itself
433 MapNode neighbor = neighbor_block->getNodeNoCheck(neighbor_rel_pos,
435 const ContentFeatures &neighbor_f = nodemgr->get(
436 neighbor.getContent());
437 u8 neighbor_light = neighbor.getLightRaw(bank, neighbor_f);
438 // If the neighbor has at least as much light as this node, then
439 // it won't lose its light, since it should have been added to
440 // from_nodes earlier, so its light would be zero.
441 if (neighbor_f.light_propagates && neighbor_light < current_light) {
442 // Unlight, but only if the node has light.
443 if (neighbor_light > 0) {
444 neighbor.setLight(bank, 0, neighbor_f);
445 neighbor_block->setNodeNoCheck(neighbor_rel_pos, neighbor);
446 from_nodes.push(neighbor_light, neighbor_rel_pos,
447 neighbor_block_pos, neighbor_block, i);
448 // The current node was modified earlier, so its block
449 // is in modified_blocks.
450 if (current.block != neighbor_block) {
451 modified_blocks[neighbor_block_pos] = neighbor_block;
455 // The neighbor can light up this node.
456 if (neighbor_light < neighbor_f.light_source) {
457 neighbor_light = neighbor_f.light_source;
459 if (brightest_neighbor_light < neighbor_light) {
460 brightest_neighbor_light = neighbor_light;
465 // If the brightest neighbor is able to light up this node,
466 // then add this node to the output nodes.
467 if (brightest_neighbor_light > 1 && f.light_propagates) {
468 brightest_neighbor_light--;
469 light_sources.push(brightest_neighbor_light, current.rel_position,
470 current.block_position, current.block,
471 (source_dir == 6) ? 6 : 5 - source_dir
472 /* with opposite direction*/);
478 * Spreads light from the specified starting nodes.
480 * Before calling this procedure, make sure that all ChangingLights
481 * in light_sources have as much light on the map as they have in
482 * light_sources (if the queue contains a node multiple times, the brightest
483 * occurrence counts).
485 * \param bank the light bank in which the procedure operates
486 * \param light_sources starting nodes
487 * \param modified_blocks output, all modified map blocks are added to this
489 void spread_light(Map *map, INodeDefManager *nodemgr, LightBank bank,
490 LightQueue &light_sources,
491 std::map<v3s16, MapBlock*> &modified_blocks)
493 // The light the current node can provide to its neighbors.
495 // The ChangingLight for the current node.
496 ChangingLight current;
497 // Position of the current neighbor.
498 mapblock_v3 neighbor_block_pos;
499 relative_v3 neighbor_rel_pos;
501 bool is_valid_position;
502 while (light_sources.next(spreading_light, current)) {
504 for (direction i = 0; i < 6; i++) {
505 // This node can't light up its light source
506 if (current.source_direction + i == 5) {
509 // Get the neighbor's position and block
510 neighbor_rel_pos = current.rel_position;
511 neighbor_block_pos = current.block_position;
512 MapBlock *neighbor_block;
513 if (step_rel_block_pos(i, neighbor_rel_pos, neighbor_block_pos)) {
514 neighbor_block = map->getBlockNoCreateNoEx(neighbor_block_pos);
515 if (neighbor_block == NULL) {
516 current.block->setLightingComplete(bank, i, false);
520 neighbor_block = current.block;
522 // Get the neighbor itself
523 MapNode neighbor = neighbor_block->getNodeNoCheck(neighbor_rel_pos,
525 const ContentFeatures &f = nodemgr->get(neighbor.getContent());
526 if (f.light_propagates) {
527 // Light up the neighbor, if it has less light than it should.
528 u8 neighbor_light = neighbor.getLightRaw(bank, f);
529 if (neighbor_light < spreading_light) {
530 neighbor.setLight(bank, spreading_light, f);
531 neighbor_block->setNodeNoCheck(neighbor_rel_pos, neighbor);
532 light_sources.push(spreading_light, neighbor_rel_pos,
533 neighbor_block_pos, neighbor_block, i);
534 // The current node was modified earlier, so its block
535 // is in modified_blocks.
536 if (current.block != neighbor_block) {
537 modified_blocks[neighbor_block_pos] = neighbor_block;
545 struct SunlightPropagationUnit{
549 SunlightPropagationUnit(v2s16 relpos, bool sunlit):
550 relative_pos(relpos),
555 struct SunlightPropagationData{
556 std::vector<SunlightPropagationUnit> data;
561 * Returns true if the node gets sunlight from the
564 * \param pos position of the node.
566 bool is_sunlight_above(Map *map, v3s16 pos, INodeDefManager *ndef)
568 bool sunlight = true;
569 mapblock_v3 source_block_pos;
570 relative_v3 source_rel_pos;
571 getNodeBlockPosWithOffset(pos + v3s16(0, 1, 0), source_block_pos,
573 // If the node above has sunlight, this node also can get it.
574 MapBlock *source_block = map->getBlockNoCreateNoEx(source_block_pos);
575 if (source_block == NULL) {
576 // But if there is no node above, then use heuristics
577 MapBlock *node_block = map->getBlockNoCreateNoEx(getNodeBlockPos(pos));
578 if (node_block == NULL) {
581 sunlight = !node_block->getIsUnderground();
584 bool is_valid_position;
585 MapNode above = source_block->getNodeNoCheck(source_rel_pos,
587 if (is_valid_position) {
588 if (above.getContent() == CONTENT_IGNORE) {
590 if (source_block->getIsUnderground()) {
593 } else if (above.getLight(LIGHTBANK_DAY, ndef) != LIGHT_SUN) {
594 // If the node above doesn't have sunlight, this
595 // node is in shadow.
603 static const LightBank banks[] = { LIGHTBANK_DAY, LIGHTBANK_NIGHT };
605 void update_lighting_nodes(Map *map,
606 std::vector<std::pair<v3s16, MapNode> > &oldnodes,
607 std::map<v3s16, MapBlock*> &modified_blocks)
609 INodeDefManager *ndef = map->getNodeDefManager();
610 // For node getter functions
611 bool is_valid_position;
613 // Process each light bank separately
614 for (s32 i = 0; i < 2; i++) {
615 LightBank bank = banks[i];
616 UnlightQueue disappearing_lights(256);
617 ReLightQueue light_sources(256);
618 // Nodes that are brighter than the brightest modified node was
619 // won't change, since they didn't get their light from a
621 u8 min_safe_light = 0;
622 for (std::vector<std::pair<v3s16, MapNode> >::iterator it =
623 oldnodes.begin(); it < oldnodes.end(); ++it) {
624 u8 old_light = it->second.getLight(bank, ndef);
625 if (old_light > min_safe_light) {
626 min_safe_light = old_light;
629 // If only one node changed, even nodes with the same brightness
630 // didn't get their light from the changed node.
631 if (oldnodes.size() > 1) {
634 // For each changed node process sunlight and initialize
635 for (std::vector<std::pair<v3s16, MapNode> >::iterator it =
636 oldnodes.begin(); it < oldnodes.end(); ++it) {
637 // Get position and block of the changed node
640 mapblock_v3 block_pos;
641 getNodeBlockPosWithOffset(p, block_pos, rel_pos);
642 MapBlock *block = map->getBlockNoCreateNoEx(block_pos);
643 if (block == NULL || block->isDummy()) {
647 MapNode n = block->getNodeNoCheck(rel_pos, &is_valid_position);
648 if (!is_valid_position) {
652 // Light of the old node
653 u8 old_light = it->second.getLight(bank, ndef);
655 // Add the block of the added node to modified_blocks
656 modified_blocks[block_pos] = block;
658 // Get new light level of the node
660 if (ndef->get(n).light_propagates) {
661 if (bank == LIGHTBANK_DAY && ndef->get(n).sunlight_propagates
662 && is_sunlight_above(map, p, ndef)) {
663 new_light = LIGHT_SUN;
665 new_light = ndef->get(n).light_source;
666 for (int i = 0; i < 6; i++) {
667 v3s16 p2 = p + neighbor_dirs[i];
669 MapNode n2 = map->getNodeNoEx(p2, &is_valid);
671 u8 spread = n2.getLight(bank, ndef);
672 // If it is sure that the neighbor won't be
673 // unlighted, its light can spread to this node.
674 if (spread > new_light && spread >= min_safe_light) {
675 new_light = spread - 1;
681 // If this is an opaque node, it still can emit light.
682 new_light = ndef->get(n).light_source;
686 light_sources.push(new_light, rel_pos, block_pos, block, 6);
689 if (new_light < old_light) {
690 // The node became opaque or doesn't provide as much
691 // light as the previous one, so it must be unlighted.
693 // Add to unlight queue
694 n.setLight(bank, 0, ndef);
695 block->setNodeNoCheck(rel_pos, n);
696 disappearing_lights.push(old_light, rel_pos, block_pos, block,
699 // Remove sunlight, if there was any
700 if (bank == LIGHTBANK_DAY && old_light == LIGHT_SUN) {
701 for (s16 y = p.Y - 1;; y--) {
702 v3s16 n2pos(p.X, y, p.Z);
706 n2 = map->getNodeNoEx(n2pos, &is_valid_position);
707 if (!is_valid_position)
710 // If this node doesn't have sunlight, the nodes below
711 // it don't have too.
712 if (n2.getLight(LIGHTBANK_DAY, ndef) != LIGHT_SUN) {
715 // Remove sunlight and add to unlight queue.
716 n2.setLight(LIGHTBANK_DAY, 0, ndef);
717 map->setNode(n2pos, n2);
718 relative_v3 rel_pos2;
719 mapblock_v3 block_pos2;
720 getNodeBlockPosWithOffset(n2pos, block_pos2, rel_pos2);
721 MapBlock *block2 = map->getBlockNoCreateNoEx(
723 disappearing_lights.push(LIGHT_SUN, rel_pos2,
725 4 /* The node above caused the change */);
728 } else if (new_light > old_light) {
729 // It is sure that the node provides more light than the previous
730 // one, unlighting is not necessary.
731 // Propagate sunlight
732 if (bank == LIGHTBANK_DAY && new_light == LIGHT_SUN) {
733 for (s16 y = p.Y - 1;; y--) {
734 v3s16 n2pos(p.X, y, p.Z);
738 n2 = map->getNodeNoEx(n2pos, &is_valid_position);
739 if (!is_valid_position)
742 // This should not happen, but if the node has sunlight
743 // then the iteration should stop.
744 if (n2.getLight(LIGHTBANK_DAY, ndef) == LIGHT_SUN) {
747 // If the node terminates sunlight, stop.
748 if (!ndef->get(n2).sunlight_propagates) {
751 relative_v3 rel_pos2;
752 mapblock_v3 block_pos2;
753 getNodeBlockPosWithOffset(n2pos, block_pos2, rel_pos2);
754 MapBlock *block2 = map->getBlockNoCreateNoEx(
756 // Mark node for lighting.
757 light_sources.push(LIGHT_SUN, rel_pos2, block_pos2,
765 unspread_light(map, ndef, bank, disappearing_lights, light_sources,
767 // Initialize light values for light spreading.
768 for (u8 i = 0; i <= LIGHT_SUN; i++) {
769 const std::vector<ChangingLight> &lights = light_sources.lights[i];
770 for (std::vector<ChangingLight>::const_iterator it = lights.begin();
771 it < lights.end(); ++it) {
772 MapNode n = it->block->getNodeNoCheck(it->rel_position,
774 n.setLight(bank, i, ndef);
775 it->block->setNodeNoCheck(it->rel_position, n);
779 spread_light(map, ndef, bank, light_sources, modified_blocks);
784 * Borders of a map block in relative node coordinates.
785 * Compatible with type 'direction'.
787 const VoxelArea block_borders[] = {
788 VoxelArea(v3s16(15, 0, 0), v3s16(15, 15, 15)), //X+
789 VoxelArea(v3s16(0, 15, 0), v3s16(15, 15, 15)), //Y+
790 VoxelArea(v3s16(0, 0, 15), v3s16(15, 15, 15)), //Z+
791 VoxelArea(v3s16(0, 0, 0), v3s16(15, 15, 0)), //Z-
792 VoxelArea(v3s16(0, 0, 0), v3s16(15, 0, 15)), //Y-
793 VoxelArea(v3s16(0, 0, 0), v3s16(0, 15, 15)) //X-
798 * -the node has unloaded neighbors
799 * -the node doesn't have light
800 * -the node's light is the same as the maximum of
801 * its light source and its brightest neighbor minus one.
804 bool is_light_locally_correct(Map *map, INodeDefManager *ndef, LightBank bank,
807 bool is_valid_position;
808 MapNode n = map->getNodeNoEx(pos, &is_valid_position);
809 const ContentFeatures &f = ndef->get(n);
810 if (f.param_type != CPT_LIGHT) {
813 u8 light = n.getLightNoChecks(bank, &f);
814 assert(f.light_source <= LIGHT_MAX);
815 u8 brightest_neighbor = f.light_source + 1;
816 for (direction d = 0; d < 6; ++d) {
817 MapNode n2 = map->getNodeNoEx(pos + neighbor_dirs[d],
819 u8 light2 = n2.getLight(bank, ndef);
820 if (brightest_neighbor < light2) {
821 brightest_neighbor = light2;
824 assert(light <= LIGHT_SUN);
825 return brightest_neighbor == light + 1;
828 void update_block_border_lighting(Map *map, MapBlock *block,
829 std::map<v3s16, MapBlock*> &modified_blocks)
831 INodeDefManager *ndef = map->getNodeDefManager();
832 bool is_valid_position;
833 for (s32 i = 0; i < 2; i++) {
834 LightBank bank = banks[i];
835 // Since invalid light is not common, do not allocate
836 // memory if not needed.
837 UnlightQueue disappearing_lights(0);
838 ReLightQueue light_sources(0);
839 // Get incorrect lights
840 for (direction d = 0; d < 6; d++) {
841 // For each direction
842 // Get neighbor block
843 v3s16 otherpos = block->getPos() + neighbor_dirs[d];
844 MapBlock *other = map->getBlockNoCreateNoEx(otherpos);
848 // Only update if lighting was not completed.
849 if (block->isLightingComplete(bank, d) &&
850 other->isLightingComplete(bank, 5 - d))
853 block->setLightingComplete(bank, d, true);
854 other->setLightingComplete(bank, 5 - d, true);
855 // The two blocks and their connecting surfaces
856 MapBlock *blocks[] = {block, other};
857 VoxelArea areas[] = {block_borders[d], block_borders[5 - d]};
859 for (u8 blocknum = 0; blocknum < 2; blocknum++) {
860 MapBlock *b = blocks[blocknum];
861 VoxelArea a = areas[blocknum];
863 for (s32 x = a.MinEdge.X; x <= a.MaxEdge.X; x++)
864 for (s32 z = a.MinEdge.Z; z <= a.MaxEdge.Z; z++)
865 for (s32 y = a.MinEdge.Y; y <= a.MaxEdge.Y; y++) {
866 MapNode n = b->getNodeNoCheck(x, y, z,
868 u8 light = n.getLight(bank, ndef);
870 if (light < LIGHT_SUN) {
871 // Unlight if not correct
872 if (!is_light_locally_correct(map, ndef, bank,
873 v3s16(x, y, z) + b->getPosRelative())) {
874 // Initialize for unlighting
875 n.setLight(bank, 0, ndef);
876 b->setNodeNoCheck(x, y, z, n);
877 modified_blocks[b->getPos()]=b;
878 disappearing_lights.push(light,
879 relative_v3(x, y, z), b->getPos(), b,
887 unspread_light(map, ndef, bank, disappearing_lights, light_sources,
889 // Initialize light values for light spreading.
890 for (u8 i = 0; i <= LIGHT_SUN; i++) {
891 const std::vector<ChangingLight> &lights = light_sources.lights[i];
892 for (std::vector<ChangingLight>::const_iterator it = lights.begin();
893 it < lights.end(); ++it) {
894 MapNode n = it->block->getNodeNoCheck(it->rel_position,
896 n.setLight(bank, i, ndef);
897 it->block->setNodeNoCheck(it->rel_position, n);
901 spread_light(map, ndef, bank, light_sources, modified_blocks);
906 * Resets the lighting of the given VoxelManipulator to
907 * complete darkness and full sunlight.
908 * Operates in one map sector.
910 * \param offset contains the least x and z node coordinates
912 * \param light incoming sunlight, light[x][z] is true if there
913 * is sunlight above the voxel manipulator at the given x-z coordinates.
914 * The array's indices are relative node coordinates in the sector.
915 * After the procedure returns, this contains outgoing light at
916 * the bottom of the voxel manipulator.
918 void fill_with_sunlight(MMVManip *vm, INodeDefManager *ndef, v2s16 offset,
919 bool light[MAP_BLOCKSIZE][MAP_BLOCKSIZE])
921 // Distance in array between two nodes on top of each other.
922 s16 ystride = vm->m_area.getExtent().X;
923 // Cache the ignore node.
924 MapNode ignore = MapNode(CONTENT_IGNORE);
925 // For each column of nodes:
926 for (s16 z = 0; z < MAP_BLOCKSIZE; z++)
927 for (s16 x = 0; x < MAP_BLOCKSIZE; x++) {
928 // Position of the column on the map.
929 v2s16 realpos = offset + v2s16(x, z);
930 // Array indices in the voxel manipulator
931 s32 maxindex = vm->m_area.index(realpos.X, vm->m_area.MaxEdge.Y,
933 s32 minindex = vm->m_area.index(realpos.X, vm->m_area.MinEdge.Y,
935 // True if the current node has sunlight.
936 bool lig = light[z][x];
937 // For each node, downwards:
938 for (s32 i = maxindex; i >= minindex; i -= ystride) {
940 if (vm->m_flags[i] & VOXELFLAG_NO_DATA)
944 // Ignore IGNORE nodes, these are not generated yet.
945 if(n->getContent() == CONTENT_IGNORE)
947 const ContentFeatures &f = ndef->get(n->getContent());
948 if (lig && !f.sunlight_propagates)
949 // Sunlight is stopped.
952 n->setLight(LIGHTBANK_DAY, lig ? 15 : 0, f);
953 n->setLight(LIGHTBANK_NIGHT, 0, f);
955 // Output outgoing light.
961 * Returns incoming sunlight for one map block.
962 * If block above is not found, it is loaded.
964 * \param pos position of the map block that gets the sunlight.
965 * \param light incoming sunlight, light[z][x] is true if there
966 * is sunlight above the block at the given z-x relative
969 void is_sunlight_above_block(ServerMap *map, mapblock_v3 pos,
970 INodeDefManager *ndef, bool light[MAP_BLOCKSIZE][MAP_BLOCKSIZE])
972 mapblock_v3 source_block_pos = pos + v3s16(0, 1, 0);
973 // Get or load source block.
974 // It might take a while to load, but correcting incorrect
975 // sunlight may be even slower.
976 MapBlock *source_block = map->emergeBlock(source_block_pos, false);
977 // Trust only generated blocks.
978 if (source_block == NULL || source_block->isDummy()
979 || !source_block->isGenerated()) {
980 // But if there is no block above, then use heuristics
981 bool sunlight = true;
982 MapBlock *node_block = map->getBlockNoCreateNoEx(pos);
983 if (node_block == NULL)
984 // This should not happen.
987 sunlight = !node_block->getIsUnderground();
988 for (s16 z = 0; z < MAP_BLOCKSIZE; z++)
989 for (s16 x = 0; x < MAP_BLOCKSIZE; x++)
990 light[z][x] = sunlight;
992 // Dummy boolean, the position is valid.
993 bool is_valid_position;
995 for (s16 z = 0; z < MAP_BLOCKSIZE; z++)
996 for (s16 x = 0; x < MAP_BLOCKSIZE; x++) {
997 // Get the bottom block.
998 MapNode above = source_block->getNodeNoCheck(x, 0, z,
1000 light[z][x] = above.getLight(LIGHTBANK_DAY, ndef) == LIGHT_SUN;
1006 * Propagates sunlight down in a given map block.
1008 * \param data contains incoming sunlight and shadow and
1009 * the coordinates of the target block.
1010 * \param unlight propagated shadow is inserted here
1011 * \param relight propagated sunlight is inserted here
1013 * \returns true if the block was modified, false otherwise.
1015 bool propagate_block_sunlight(Map *map, INodeDefManager *ndef,
1016 SunlightPropagationData *data, UnlightQueue *unlight, ReLightQueue *relight)
1018 bool modified = false;
1020 MapBlock *block = map->getBlockNoCreateNoEx(data->target_block);
1021 if (block == NULL || block->isDummy()) {
1022 // The work is done if the block does not contain data.
1028 // For each changing column of nodes:
1030 for (index = 0; index < data->data.size(); index++) {
1031 SunlightPropagationUnit it = data->data[index];
1032 // Relative position of the currently inspected node.
1033 relative_v3 current_pos(it.relative_pos.X, MAP_BLOCKSIZE - 1,
1036 // Propagate sunlight.
1037 // For each node downwards:
1038 for (; current_pos.Y >= 0; current_pos.Y--) {
1039 MapNode n = block->getNodeNoCheck(current_pos, &is_valid);
1040 const ContentFeatures &f = ndef->get(n);
1041 if (n.getLightRaw(LIGHTBANK_DAY, f) < LIGHT_SUN
1042 && f.sunlight_propagates) {
1043 // This node gets sunlight.
1044 n.setLight(LIGHTBANK_DAY, LIGHT_SUN, f);
1045 block->setNodeNoCheck(current_pos, n);
1047 relight->push(LIGHT_SUN, current_pos, data->target_block,
1050 // Light already valid, propagation stopped.
1055 // Propagate shadow.
1056 // For each node downwards:
1057 for (; current_pos.Y >= 0; current_pos.Y--) {
1058 MapNode n = block->getNodeNoCheck(current_pos, &is_valid);
1059 const ContentFeatures &f = ndef->get(n);
1060 if (n.getLightRaw(LIGHTBANK_DAY, f) == LIGHT_SUN) {
1061 // The sunlight is no longer valid.
1062 n.setLight(LIGHTBANK_DAY, 0, f);
1063 block->setNodeNoCheck(current_pos, n);
1065 unlight->push(LIGHT_SUN, current_pos, data->target_block,
1068 // Reached shadow, propagation stopped.
1073 if (current_pos.Y >= 0) {
1074 // Propagation stopped, remove from data.
1075 data->data[index] = data->data.back();
1076 data->data.pop_back();
1084 * Borders of a map block in relative node coordinates.
1085 * The areas do not overlap.
1086 * Compatible with type 'direction'.
1088 const VoxelArea block_pad[] = {
1089 VoxelArea(v3s16(15, 0, 0), v3s16(15, 15, 15)), //X+
1090 VoxelArea(v3s16(1, 15, 0), v3s16(14, 15, 15)), //Y+
1091 VoxelArea(v3s16(1, 1, 15), v3s16(14, 14, 15)), //Z+
1092 VoxelArea(v3s16(1, 1, 0), v3s16(14, 14, 0)), //Z-
1093 VoxelArea(v3s16(1, 0, 0), v3s16(14, 0, 15)), //Y-
1094 VoxelArea(v3s16(0, 0, 0), v3s16(0, 15, 15)) //X-
1098 * The common part of bulk light updates - it is always executed.
1099 * The procedure takes the nodes that should be unlit, and the
1100 * full modified area.
1102 * The procedure handles the correction of all lighting except
1103 * direct sunlight spreading.
1105 * \param minblock least coordinates of the changed area in block
1107 * \param maxblock greatest coordinates of the changed area in block
1109 * \param unlight the first queue is for day light, the second is for
1110 * night light. Contains all nodes on the borders that need to be unlit.
1111 * \param relight the first queue is for day light, the second is for
1112 * night light. Contains nodes that were not modified, but got sunlight
1113 * because the changes.
1114 * \param modified_blocks the procedure adds all modified blocks to
1117 void finish_bulk_light_update(Map *map, mapblock_v3 minblock,
1118 mapblock_v3 maxblock, UnlightQueue unlight[2], ReLightQueue relight[2],
1119 std::map<v3s16, MapBlock*> *modified_blocks)
1121 INodeDefManager *ndef = map->getNodeDefManager();
1125 // --- STEP 1: Do unlighting
1127 for (size_t bank = 0; bank < 2; bank++) {
1128 LightBank b = banks[bank];
1129 unspread_light(map, ndef, b, unlight[bank], relight[bank],
1133 // --- STEP 2: Get all newly inserted light sources
1136 for (s16 b_x = minblock.X; b_x <= maxblock.X; b_x++)
1137 for (s16 b_y = minblock.Y; b_y <= maxblock.Y; b_y++)
1138 for (s16 b_z = minblock.Z; b_z <= maxblock.Z; b_z++) {
1139 const v3s16 blockpos(b_x, b_y, b_z);
1140 MapBlock *block = map->getBlockNoCreateNoEx(blockpos);
1141 if (!block || block->isDummy())
1142 // Skip not existing blocks
1144 // For each node in the block:
1145 for (s32 x = 0; x < MAP_BLOCKSIZE; x++)
1146 for (s32 z = 0; z < MAP_BLOCKSIZE; z++)
1147 for (s32 y = 0; y < MAP_BLOCKSIZE; y++) {
1148 v3s16 relpos(x, y, z);
1149 MapNode node = block->getNodeNoCheck(x, y, z, &is_valid);
1150 const ContentFeatures &f = ndef->get(node);
1151 // For each light bank
1152 for (size_t b = 0; b < 2; b++) {
1153 LightBank bank = banks[b];
1154 u8 light = f.param_type == CPT_LIGHT ?
1155 node.getLightNoChecks(bank, &f):
1158 relight[b].push(light, relpos, blockpos, block, 6);
1163 // --- STEP 3: do light spreading
1165 // For each light bank:
1166 for (size_t b = 0; b < 2; b++) {
1167 LightBank bank = banks[b];
1168 // Sunlight is already initialized.
1169 u8 maxlight = (b == 0) ? LIGHT_MAX : LIGHT_SUN;
1170 // Initialize light values for light spreading.
1171 for (u8 i = 0; i <= maxlight; i++) {
1172 const std::vector<ChangingLight> &lights = relight[b].lights[i];
1173 for (std::vector<ChangingLight>::const_iterator it = lights.begin();
1174 it < lights.end(); ++it) {
1175 MapNode n = it->block->getNodeNoCheck(it->rel_position,
1177 n.setLight(bank, i, ndef);
1178 it->block->setNodeNoCheck(it->rel_position, n);
1182 spread_light(map, ndef, bank, relight[b], *modified_blocks);
1186 void blit_back_with_light(ServerMap *map, MMVManip *vm,
1187 std::map<v3s16, MapBlock*> *modified_blocks)
1189 INodeDefManager *ndef = map->getNodeDefManager();
1190 mapblock_v3 minblock = getNodeBlockPos(vm->m_area.MinEdge);
1191 mapblock_v3 maxblock = getNodeBlockPos(vm->m_area.MaxEdge);
1192 // First queue is for day light, second is for night light.
1193 UnlightQueue unlight[] = { UnlightQueue(256), UnlightQueue(256) };
1194 ReLightQueue relight[] = { ReLightQueue(256), ReLightQueue(256) };
1195 // Will hold sunlight data.
1196 bool lights[MAP_BLOCKSIZE][MAP_BLOCKSIZE];
1197 SunlightPropagationData data;
1201 // --- STEP 1: reset everything to sunlight
1203 // For each map block:
1204 for (s16 x = minblock.X; x <= maxblock.X; x++)
1205 for (s16 z = minblock.Z; z <= maxblock.Z; z++) {
1206 // Extract sunlight above.
1207 is_sunlight_above_block(map, v3s16(x, maxblock.Y, z), ndef, lights);
1209 offset *= MAP_BLOCKSIZE;
1210 // Reset the voxel manipulator.
1211 fill_with_sunlight(vm, ndef, offset, lights);
1212 // Copy sunlight data
1213 data.target_block = v3s16(x, minblock.Y - 1, z);
1214 for (s16 z = 0; z < MAP_BLOCKSIZE; z++)
1215 for (s16 x = 0; x < MAP_BLOCKSIZE; x++)
1216 data.data.push_back(
1217 SunlightPropagationUnit(v2s16(x, z), lights[z][x]));
1218 // Propagate sunlight and shadow below the voxel manipulator.
1219 while (!data.data.empty()) {
1220 if (propagate_block_sunlight(map, ndef, &data, &unlight[0],
1222 (*modified_blocks)[data.target_block] =
1223 map->getBlockNoCreateNoEx(data.target_block);
1225 data.target_block.Y--;
1229 // --- STEP 2: Get nodes from borders to unlight
1231 // In case there are unloaded holes in the voxel manipulator
1232 // unlight each block.
1234 for (s16 b_x = minblock.X; b_x <= maxblock.X; b_x++)
1235 for (s16 b_y = minblock.Y; b_y <= maxblock.Y; b_y++)
1236 for (s16 b_z = minblock.Z; b_z <= maxblock.Z; b_z++) {
1237 v3s16 blockpos(b_x, b_y, b_z);
1238 MapBlock *block = map->getBlockNoCreateNoEx(blockpos);
1239 if (!block || block->isDummy())
1240 // Skip not existing blocks.
1242 v3s16 offset = block->getPosRelative();
1243 // For each border of the block:
1244 for (direction d = 0; d < 6; d++) {
1245 VoxelArea a = block_pad[d];
1246 // For each node of the border:
1247 for (s32 x = a.MinEdge.X; x <= a.MaxEdge.X; x++)
1248 for (s32 z = a.MinEdge.Z; z <= a.MaxEdge.Z; z++)
1249 for (s32 y = a.MinEdge.Y; y <= a.MaxEdge.Y; y++) {
1250 v3s16 relpos(x, y, z);
1251 // Get old and new node
1252 MapNode oldnode = block->getNodeNoCheck(x, y, z, &is_valid);
1253 const ContentFeatures &oldf = ndef->get(oldnode);
1254 MapNode newnode = vm->getNodeNoExNoEmerge(relpos + offset);
1255 const ContentFeatures &newf = ndef->get(newnode);
1256 // For each light bank
1257 for (size_t b = 0; b < 2; b++) {
1258 LightBank bank = banks[b];
1259 u8 oldlight = oldf.param_type == CPT_LIGHT ?
1260 oldnode.getLightNoChecks(bank, &oldf):
1261 LIGHT_SUN; // no light information, force unlighting
1262 u8 newlight = newf.param_type == CPT_LIGHT ?
1263 newnode.getLightNoChecks(bank, &newf):
1265 // If the new node is dimmer, unlight.
1266 if (oldlight > newlight) {
1268 oldlight, relpos, blockpos, block, 6);
1275 // --- STEP 3: All information extracted, overwrite
1277 vm->blitBackAll(modified_blocks, true);
1279 // --- STEP 4: Finish light update
1281 finish_bulk_light_update(map, minblock, maxblock, unlight, relight,
1286 * Resets the lighting of the given map block to
1287 * complete darkness and full sunlight.
1289 * \param light incoming sunlight, light[x][z] is true if there
1290 * is sunlight above the map block at the given x-z coordinates.
1291 * The array's indices are relative node coordinates in the block.
1292 * After the procedure returns, this contains outgoing light at
1293 * the bottom of the map block.
1295 void fill_with_sunlight(MapBlock *block, INodeDefManager *ndef,
1296 bool light[MAP_BLOCKSIZE][MAP_BLOCKSIZE])
1298 if (block->isDummy())
1302 // For each column of nodes:
1303 for (s16 z = 0; z < MAP_BLOCKSIZE; z++)
1304 for (s16 x = 0; x < MAP_BLOCKSIZE; x++) {
1305 // True if the current node has sunlight.
1306 bool lig = light[z][x];
1307 // For each node, downwards:
1308 for (s16 y = MAP_BLOCKSIZE - 1; y >= 0; y--) {
1309 MapNode n = block->getNodeNoCheck(x, y, z, &is_valid);
1310 // Ignore IGNORE nodes, these are not generated yet.
1311 if (n.getContent() == CONTENT_IGNORE)
1313 const ContentFeatures &f = ndef->get(n.getContent());
1314 if (lig && !f.sunlight_propagates) {
1315 // Sunlight is stopped.
1319 n.setLight(LIGHTBANK_DAY, lig ? 15 : 0, f);
1320 n.setLight(LIGHTBANK_NIGHT, 0, f);
1321 block->setNodeNoCheck(x, y, z, n);
1323 // Output outgoing light.
1328 void repair_block_light(ServerMap *map, MapBlock *block,
1329 std::map<v3s16, MapBlock*> *modified_blocks)
1331 if (!block || block->isDummy())
1333 INodeDefManager *ndef = map->getNodeDefManager();
1334 // First queue is for day light, second is for night light.
1335 UnlightQueue unlight[] = { UnlightQueue(256), UnlightQueue(256) };
1336 ReLightQueue relight[] = { ReLightQueue(256), ReLightQueue(256) };
1337 // Will hold sunlight data.
1338 bool lights[MAP_BLOCKSIZE][MAP_BLOCKSIZE];
1339 SunlightPropagationData data;
1343 // --- STEP 1: reset everything to sunlight
1345 mapblock_v3 blockpos = block->getPos();
1346 (*modified_blocks)[blockpos] = block;
1347 // For each map block:
1348 // Extract sunlight above.
1349 is_sunlight_above_block(map, blockpos, ndef, lights);
1350 // Reset the voxel manipulator.
1351 fill_with_sunlight(block, ndef, lights);
1352 // Copy sunlight data
1353 data.target_block = v3s16(blockpos.X, blockpos.Y - 1, blockpos.Z);
1354 for (s16 z = 0; z < MAP_BLOCKSIZE; z++)
1355 for (s16 x = 0; x < MAP_BLOCKSIZE; x++) {
1356 data.data.push_back(
1357 SunlightPropagationUnit(v2s16(x, z), lights[z][x]));
1359 // Propagate sunlight and shadow below the voxel manipulator.
1360 while (!data.data.empty()) {
1361 if (propagate_block_sunlight(map, ndef, &data, &unlight[0],
1363 (*modified_blocks)[data.target_block] =
1364 map->getBlockNoCreateNoEx(data.target_block);
1366 data.target_block.Y--;
1369 // --- STEP 2: Get nodes from borders to unlight
1371 // For each border of the block:
1372 for (direction d = 0; d < 6; d++) {
1373 VoxelArea a = block_pad[d];
1374 // For each node of the border:
1375 for (s32 x = a.MinEdge.X; x <= a.MaxEdge.X; x++)
1376 for (s32 z = a.MinEdge.Z; z <= a.MaxEdge.Z; z++)
1377 for (s32 y = a.MinEdge.Y; y <= a.MaxEdge.Y; y++) {
1378 v3s16 relpos(x, y, z);
1380 MapNode node = block->getNodeNoCheck(x, y, z, &is_valid);
1381 const ContentFeatures &f = ndef->get(node);
1382 // For each light bank
1383 for (size_t b = 0; b < 2; b++) {
1384 LightBank bank = banks[b];
1385 u8 light = f.param_type == CPT_LIGHT ?
1386 node.getLightNoChecks(bank, &f):
1388 // If the new node is dimmer than sunlight, unlight.
1389 // (if it has maximal light, it is pointless to remove
1390 // surrounding light, as it can only become brighter)
1391 if (LIGHT_SUN > light) {
1393 LIGHT_SUN, relpos, blockpos, block, 6);
1399 // STEP 3: Remove and spread light
1401 finish_bulk_light_update(map, blockpos, blockpos, unlight, relight,
1405 VoxelLineIterator::VoxelLineIterator(const v3f &start_position, const v3f &line_vector) :
1406 m_start_position(start_position),
1407 m_line_vector(line_vector)
1409 m_current_node_pos = floatToInt(m_start_position, 1);
1411 if (m_line_vector.X > 0) {
1412 m_next_intersection_multi.X = (floorf(m_start_position.X - 0.5) + 1.5
1413 - m_start_position.X) / m_line_vector.X;
1414 m_intersection_multi_inc.X = 1 / m_line_vector.X;
1415 } else if (m_line_vector.X < 0) {
1416 m_next_intersection_multi.X = (floorf(m_start_position.X - 0.5)
1417 - m_start_position.X + 0.5) / m_line_vector.X;
1418 m_intersection_multi_inc.X = -1 / m_line_vector.X;
1419 m_step_directions.X = -1;
1422 if (m_line_vector.Y > 0) {
1423 m_next_intersection_multi.Y = (floorf(m_start_position.Y - 0.5) + 1.5
1424 - m_start_position.Y) / m_line_vector.Y;
1425 m_intersection_multi_inc.Y = 1 / m_line_vector.Y;
1426 } else if (m_line_vector.Y < 0) {
1427 m_next_intersection_multi.Y = (floorf(m_start_position.Y - 0.5)
1428 - m_start_position.Y + 0.5) / m_line_vector.Y;
1429 m_intersection_multi_inc.Y = -1 / m_line_vector.Y;
1430 m_step_directions.Y = -1;
1433 if (m_line_vector.Z > 0) {
1434 m_next_intersection_multi.Z = (floorf(m_start_position.Z - 0.5) + 1.5
1435 - m_start_position.Z) / m_line_vector.Z;
1436 m_intersection_multi_inc.Z = 1 / m_line_vector.Z;
1437 } else if (m_line_vector.Z < 0) {
1438 m_next_intersection_multi.Z = (floorf(m_start_position.Z - 0.5)
1439 - m_start_position.Z + 0.5) / m_line_vector.Z;
1440 m_intersection_multi_inc.Z = -1 / m_line_vector.Z;
1441 m_step_directions.Z = -1;
1444 m_has_next = (m_next_intersection_multi.X <= 1)
1445 || (m_next_intersection_multi.Y <= 1)
1446 || (m_next_intersection_multi.Z <= 1);
1449 void VoxelLineIterator::next()
1451 if ((m_next_intersection_multi.X < m_next_intersection_multi.Y)
1452 && (m_next_intersection_multi.X < m_next_intersection_multi.Z)) {
1453 m_next_intersection_multi.X += m_intersection_multi_inc.X;
1454 m_current_node_pos.X += m_step_directions.X;
1455 } else if ((m_next_intersection_multi.Y < m_next_intersection_multi.Z)) {
1456 m_next_intersection_multi.Y += m_intersection_multi_inc.Y;
1457 m_current_node_pos.Y += m_step_directions.Y;
1459 m_next_intersection_multi.Z += m_intersection_multi_inc.Z;
1460 m_current_node_pos.Z += m_step_directions.Z;
1463 m_has_next = (m_next_intersection_multi.X <= 1)
1464 || (m_next_intersection_multi.Y <= 1)
1465 || (m_next_intersection_multi.Z <= 1);
1468 } // namespace voxalgo