cleanup
[waspsaliva.git] / src / voxelalgorithms.cpp
blob62fd688903ffc869af469e5e0fad5e1e8e979c42
1 /*
2 Minetest
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"
21 #include "nodedef.h"
22 #include "mapblock.h"
23 #include "map.h"
25 namespace voxalgo
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.
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.
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.
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;
60 /*!
61 * Direction from the node that caused this node's changing
62 * to this node.
64 direction source_direction = 6;
66 ChangingLight() = default;
68 ChangingLight(const relative_v3 &rel_pos, const 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)
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.
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;
88 /*!
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
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.
109 bool next(u8 &light, ChangingLight &data)
111 while (lights[max_light].empty()) {
112 if (max_light == 0) {
113 return false;
115 max_light--;
117 light = max_light;
118 data = lights[max_light].back();
119 lights[max_light].pop_back();
120 return true;
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, const relative_v3 &rel_pos,
129 const 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
152 * too.
154 typedef LightQueue ReLightQueue;
157 * neighbor_dirs[i] points towards
158 * the direction i.
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)
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;
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;
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;
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;
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;
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;
234 break;
236 return false;
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
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 // A dummy boolean
260 bool is_valid_position;
261 // Direction of the brightest neighbor of the node
262 direction source_dir;
263 while (from_nodes.next(current_light, current)) {
264 // For all nodes that need unlighting
266 // There is no brightest neighbor
267 source_dir = 6;
268 // The current node
269 const MapNode &node = current.block->getNodeNoCheck(
270 current.rel_position, &is_valid_position);
271 const ContentFeatures &f = nodemgr->get(node);
272 // If the node emits light, it behaves like it had a
273 // brighter neighbor.
274 u8 brightest_neighbor_light = f.light_source + 1;
275 for (direction i = 0; i < 6; i++) {
276 //For each neighbor
278 // The node that changed this node has already zero light
279 // and it can't give light to this node
280 if (current.source_direction + i == 5) {
281 continue;
283 // Get the neighbor's position and block
284 neighbor_rel_pos = current.rel_position;
285 neighbor_block_pos = current.block_position;
286 MapBlock *neighbor_block;
287 if (step_rel_block_pos(i, neighbor_rel_pos, neighbor_block_pos)) {
288 neighbor_block = map->getBlockNoCreateNoEx(neighbor_block_pos);
289 if (neighbor_block == NULL) {
290 current.block->setLightingComplete(bank, i, false);
291 continue;
293 } else {
294 neighbor_block = current.block;
296 // Get the neighbor itself
297 MapNode neighbor = neighbor_block->getNodeNoCheck(neighbor_rel_pos,
298 &is_valid_position);
299 const ContentFeatures &neighbor_f = nodemgr->get(
300 neighbor.getContent());
301 u8 neighbor_light = neighbor.getLightRaw(bank, neighbor_f);
302 // If the neighbor has at least as much light as this node, then
303 // it won't lose its light, since it should have been added to
304 // from_nodes earlier, so its light would be zero.
305 if (neighbor_f.light_propagates && neighbor_light < current_light) {
306 // Unlight, but only if the node has light.
307 if (neighbor_light > 0) {
308 neighbor.setLight(bank, 0, neighbor_f);
309 neighbor_block->setNodeNoCheck(neighbor_rel_pos, neighbor);
310 from_nodes.push(neighbor_light, neighbor_rel_pos,
311 neighbor_block_pos, neighbor_block, i);
312 // The current node was modified earlier, so its block
313 // is in modified_blocks.
314 if (current.block != neighbor_block) {
315 modified_blocks[neighbor_block_pos] = neighbor_block;
318 } else {
319 // The neighbor can light up this node.
320 if (neighbor_light < neighbor_f.light_source) {
321 neighbor_light = neighbor_f.light_source;
323 if (brightest_neighbor_light < neighbor_light) {
324 brightest_neighbor_light = neighbor_light;
325 source_dir = i;
329 // If the brightest neighbor is able to light up this node,
330 // then add this node to the output nodes.
331 if (brightest_neighbor_light > 1 && f.light_propagates) {
332 brightest_neighbor_light--;
333 light_sources.push(brightest_neighbor_light, current.rel_position,
334 current.block_position, current.block,
335 (source_dir == 6) ? 6 : 5 - source_dir
336 /* with opposite direction*/);
342 * Spreads light from the specified starting nodes.
344 * Before calling this procedure, make sure that all ChangingLights
345 * in light_sources have as much light on the map as they have in
346 * light_sources (if the queue contains a node multiple times, the brightest
347 * occurrence counts).
349 * \param bank the light bank in which the procedure operates
350 * \param light_sources starting nodes
351 * \param modified_blocks output, all modified map blocks are added to this
353 void spread_light(Map *map, const NodeDefManager *nodemgr, LightBank bank,
354 LightQueue &light_sources,
355 std::map<v3s16, MapBlock*> &modified_blocks)
357 // The light the current node can provide to its neighbors.
358 u8 spreading_light;
359 // The ChangingLight for the current node.
360 ChangingLight current;
361 // Position of the current neighbor.
362 mapblock_v3 neighbor_block_pos;
363 relative_v3 neighbor_rel_pos;
364 // A dummy boolean.
365 bool is_valid_position;
366 while (light_sources.next(spreading_light, current)) {
367 spreading_light--;
368 for (direction i = 0; i < 6; i++) {
369 // This node can't light up its light source
370 if (current.source_direction + i == 5) {
371 continue;
373 // Get the neighbor's position and block
374 neighbor_rel_pos = current.rel_position;
375 neighbor_block_pos = current.block_position;
376 MapBlock *neighbor_block;
377 if (step_rel_block_pos(i, neighbor_rel_pos, neighbor_block_pos)) {
378 neighbor_block = map->getBlockNoCreateNoEx(neighbor_block_pos);
379 if (neighbor_block == NULL) {
380 current.block->setLightingComplete(bank, i, false);
381 continue;
383 } else {
384 neighbor_block = current.block;
386 // Get the neighbor itself
387 MapNode neighbor = neighbor_block->getNodeNoCheck(neighbor_rel_pos,
388 &is_valid_position);
389 const ContentFeatures &f = nodemgr->get(neighbor.getContent());
390 if (f.light_propagates) {
391 // Light up the neighbor, if it has less light than it should.
392 u8 neighbor_light = neighbor.getLightRaw(bank, f);
393 if (neighbor_light < spreading_light) {
394 neighbor.setLight(bank, spreading_light, f);
395 neighbor_block->setNodeNoCheck(neighbor_rel_pos, neighbor);
396 light_sources.push(spreading_light, neighbor_rel_pos,
397 neighbor_block_pos, neighbor_block, i);
398 // The current node was modified earlier, so its block
399 // is in modified_blocks.
400 if (current.block != neighbor_block) {
401 modified_blocks[neighbor_block_pos] = neighbor_block;
409 struct SunlightPropagationUnit{
410 v2s16 relative_pos;
411 bool is_sunlit;
413 SunlightPropagationUnit(v2s16 relpos, bool sunlit):
414 relative_pos(relpos),
415 is_sunlit(sunlit)
419 struct SunlightPropagationData{
420 std::vector<SunlightPropagationUnit> data;
421 v3s16 target_block;
425 * Returns true if the node gets sunlight from the
426 * node above it.
428 * \param pos position of the node.
430 bool is_sunlight_above(Map *map, v3s16 pos, const NodeDefManager *ndef)
432 bool sunlight = true;
433 mapblock_v3 source_block_pos;
434 relative_v3 source_rel_pos;
435 getNodeBlockPosWithOffset(pos + v3s16(0, 1, 0), source_block_pos,
436 source_rel_pos);
437 // If the node above has sunlight, this node also can get it.
438 MapBlock *source_block = map->getBlockNoCreateNoEx(source_block_pos);
439 if (source_block == NULL) {
440 // But if there is no node above, then use heuristics
441 MapBlock *node_block = map->getBlockNoCreateNoEx(getNodeBlockPos(pos));
442 if (node_block == NULL) {
443 sunlight = false;
444 } else {
445 sunlight = !node_block->getIsUnderground();
447 } else {
448 bool is_valid_position;
449 MapNode above = source_block->getNodeNoCheck(source_rel_pos,
450 &is_valid_position);
451 if (is_valid_position) {
452 if (above.getContent() == CONTENT_IGNORE) {
453 // Trust heuristics
454 if (source_block->getIsUnderground()) {
455 sunlight = false;
457 } else if (above.getLight(LIGHTBANK_DAY, ndef) != LIGHT_SUN) {
458 // If the node above doesn't have sunlight, this
459 // node is in shadow.
460 sunlight = false;
464 return sunlight;
467 static const LightBank banks[] = { LIGHTBANK_DAY, LIGHTBANK_NIGHT };
469 void update_lighting_nodes(Map *map,
470 std::vector<std::pair<v3s16, MapNode> > &oldnodes,
471 std::map<v3s16, MapBlock*> &modified_blocks)
473 const NodeDefManager *ndef = map->getNodeDefManager();
474 // For node getter functions
475 bool is_valid_position;
477 // Process each light bank separately
478 for (LightBank bank : banks) {
479 UnlightQueue disappearing_lights(256);
480 ReLightQueue light_sources(256);
481 // Nodes that are brighter than the brightest modified node was
482 // won't change, since they didn't get their light from a
483 // modified node.
484 u8 min_safe_light = 0;
485 for (std::vector<std::pair<v3s16, MapNode> >::iterator it =
486 oldnodes.begin(); it < oldnodes.end(); ++it) {
487 u8 old_light = it->second.getLight(bank, ndef);
488 if (old_light > min_safe_light) {
489 min_safe_light = old_light;
492 // If only one node changed, even nodes with the same brightness
493 // didn't get their light from the changed node.
494 if (oldnodes.size() > 1) {
495 min_safe_light++;
497 // For each changed node process sunlight and initialize
498 for (std::vector<std::pair<v3s16, MapNode> >::iterator it =
499 oldnodes.begin(); it < oldnodes.end(); ++it) {
500 // Get position and block of the changed node
501 v3s16 p = it->first;
502 relative_v3 rel_pos;
503 mapblock_v3 block_pos;
504 getNodeBlockPosWithOffset(p, block_pos, rel_pos);
505 MapBlock *block = map->getBlockNoCreateNoEx(block_pos);
506 if (block == NULL || block->isDummy()) {
507 continue;
509 // Get the new node
510 MapNode n = block->getNodeNoCheck(rel_pos, &is_valid_position);
511 if (!is_valid_position) {
512 break;
515 // Light of the old node
516 u8 old_light = it->second.getLight(bank, ndef);
518 // Add the block of the added node to modified_blocks
519 modified_blocks[block_pos] = block;
521 // Get new light level of the node
522 u8 new_light = 0;
523 if (ndef->get(n).light_propagates) {
524 if (bank == LIGHTBANK_DAY && ndef->get(n).sunlight_propagates
525 && is_sunlight_above(map, p, ndef)) {
526 new_light = LIGHT_SUN;
527 } else {
528 new_light = ndef->get(n).light_source;
529 for (const v3s16 &neighbor_dir : neighbor_dirs) {
530 v3s16 p2 = p + neighbor_dir;
531 bool is_valid;
532 MapNode n2 = map->getNode(p2, &is_valid);
533 if (is_valid) {
534 u8 spread = n2.getLight(bank, ndef);
535 // If it is sure that the neighbor won't be
536 // unlighted, its light can spread to this node.
537 if (spread > new_light && spread >= min_safe_light) {
538 new_light = spread - 1;
543 } else {
544 // If this is an opaque node, it still can emit light.
545 new_light = ndef->get(n).light_source;
548 if (new_light > 0) {
549 light_sources.push(new_light, rel_pos, block_pos, block, 6);
552 if (new_light < old_light) {
553 // The node became opaque or doesn't provide as much
554 // light as the previous one, so it must be unlighted.
556 // Add to unlight queue
557 n.setLight(bank, 0, ndef);
558 block->setNodeNoCheck(rel_pos, n);
559 disappearing_lights.push(old_light, rel_pos, block_pos, block,
562 // Remove sunlight, if there was any
563 if (bank == LIGHTBANK_DAY && old_light == LIGHT_SUN) {
564 for (s16 y = p.Y - 1;; y--) {
565 v3s16 n2pos(p.X, y, p.Z);
567 MapNode n2;
569 n2 = map->getNode(n2pos, &is_valid_position);
570 if (!is_valid_position)
571 break;
573 // If this node doesn't have sunlight, the nodes below
574 // it don't have too.
575 if (n2.getLight(LIGHTBANK_DAY, ndef) != LIGHT_SUN) {
576 break;
578 // Remove sunlight and add to unlight queue.
579 n2.setLight(LIGHTBANK_DAY, 0, ndef);
580 map->setNode(n2pos, n2);
581 relative_v3 rel_pos2;
582 mapblock_v3 block_pos2;
583 getNodeBlockPosWithOffset(n2pos, block_pos2, rel_pos2);
584 MapBlock *block2 = map->getBlockNoCreateNoEx(
585 block_pos2);
586 disappearing_lights.push(LIGHT_SUN, rel_pos2,
587 block_pos2, block2,
588 4 /* The node above caused the change */);
591 } else if (new_light > old_light) {
592 // It is sure that the node provides more light than the previous
593 // one, unlighting is not necessary.
594 // Propagate sunlight
595 if (bank == LIGHTBANK_DAY && new_light == LIGHT_SUN) {
596 for (s16 y = p.Y - 1;; y--) {
597 v3s16 n2pos(p.X, y, p.Z);
599 MapNode n2;
601 n2 = map->getNode(n2pos, &is_valid_position);
602 if (!is_valid_position)
603 break;
605 // This should not happen, but if the node has sunlight
606 // then the iteration should stop.
607 if (n2.getLight(LIGHTBANK_DAY, ndef) == LIGHT_SUN) {
608 break;
610 // If the node terminates sunlight, stop.
611 if (!ndef->get(n2).sunlight_propagates) {
612 break;
614 relative_v3 rel_pos2;
615 mapblock_v3 block_pos2;
616 getNodeBlockPosWithOffset(n2pos, block_pos2, rel_pos2);
617 MapBlock *block2 = map->getBlockNoCreateNoEx(
618 block_pos2);
619 // Mark node for lighting.
620 light_sources.push(LIGHT_SUN, rel_pos2, block_pos2,
621 block2, 4);
627 // Remove lights
628 unspread_light(map, ndef, bank, disappearing_lights, light_sources,
629 modified_blocks);
630 // Initialize light values for light spreading.
631 for (u8 i = 0; i <= LIGHT_SUN; i++) {
632 const std::vector<ChangingLight> &lights = light_sources.lights[i];
633 for (std::vector<ChangingLight>::const_iterator it = lights.begin();
634 it < lights.end(); ++it) {
635 MapNode n = it->block->getNodeNoCheck(it->rel_position,
636 &is_valid_position);
637 n.setLight(bank, i, ndef);
638 it->block->setNodeNoCheck(it->rel_position, n);
641 // Spread lights.
642 spread_light(map, ndef, bank, light_sources, modified_blocks);
647 * Borders of a map block in relative node coordinates.
648 * Compatible with type 'direction'.
650 const VoxelArea block_borders[] = {
651 VoxelArea(v3s16(15, 0, 0), v3s16(15, 15, 15)), //X+
652 VoxelArea(v3s16(0, 15, 0), v3s16(15, 15, 15)), //Y+
653 VoxelArea(v3s16(0, 0, 15), v3s16(15, 15, 15)), //Z+
654 VoxelArea(v3s16(0, 0, 0), v3s16(15, 15, 0)), //Z-
655 VoxelArea(v3s16(0, 0, 0), v3s16(15, 0, 15)), //Y-
656 VoxelArea(v3s16(0, 0, 0), v3s16(0, 15, 15)) //X-
660 * Returns true if:
661 * -the node has unloaded neighbors
662 * -the node doesn't have light
663 * -the node's light is the same as the maximum of
664 * its light source and its brightest neighbor minus one.
667 bool is_light_locally_correct(Map *map, const NodeDefManager *ndef,
668 LightBank bank, v3s16 pos)
670 bool is_valid_position;
671 MapNode n = map->getNode(pos, &is_valid_position);
672 const ContentFeatures &f = ndef->get(n);
673 if (f.param_type != CPT_LIGHT) {
674 return true;
676 u8 light = n.getLightNoChecks(bank, &f);
677 assert(f.light_source <= LIGHT_MAX);
678 u8 brightest_neighbor = f.light_source + 1;
679 for (const v3s16 &neighbor_dir : neighbor_dirs) {
680 MapNode n2 = map->getNode(pos + neighbor_dir,
681 &is_valid_position);
682 u8 light2 = n2.getLight(bank, ndef);
683 if (brightest_neighbor < light2) {
684 brightest_neighbor = light2;
687 assert(light <= LIGHT_SUN);
688 return brightest_neighbor == light + 1;
691 void update_block_border_lighting(Map *map, MapBlock *block,
692 std::map<v3s16, MapBlock*> &modified_blocks)
694 const NodeDefManager *ndef = map->getNodeDefManager();
695 bool is_valid_position;
696 for (LightBank bank : banks) {
697 // Since invalid light is not common, do not allocate
698 // memory if not needed.
699 UnlightQueue disappearing_lights(0);
700 ReLightQueue light_sources(0);
701 // Get incorrect lights
702 for (direction d = 0; d < 6; d++) {
703 // For each direction
704 // Get neighbor block
705 v3s16 otherpos = block->getPos() + neighbor_dirs[d];
706 MapBlock *other = map->getBlockNoCreateNoEx(otherpos);
707 if (other == NULL) {
708 continue;
710 // Only update if lighting was not completed.
711 if (block->isLightingComplete(bank, d) &&
712 other->isLightingComplete(bank, 5 - d))
713 continue;
714 // Reset flags
715 block->setLightingComplete(bank, d, true);
716 other->setLightingComplete(bank, 5 - d, true);
717 // The two blocks and their connecting surfaces
718 MapBlock *blocks[] = {block, other};
719 VoxelArea areas[] = {block_borders[d], block_borders[5 - d]};
720 // For both blocks
721 for (u8 blocknum = 0; blocknum < 2; blocknum++) {
722 MapBlock *b = blocks[blocknum];
723 VoxelArea a = areas[blocknum];
724 // For all nodes
725 for (s32 x = a.MinEdge.X; x <= a.MaxEdge.X; x++)
726 for (s32 z = a.MinEdge.Z; z <= a.MaxEdge.Z; z++)
727 for (s32 y = a.MinEdge.Y; y <= a.MaxEdge.Y; y++) {
728 MapNode n = b->getNodeNoCheck(x, y, z,
729 &is_valid_position);
730 u8 light = n.getLight(bank, ndef);
731 // Sunlight is fixed
732 if (light < LIGHT_SUN) {
733 // Unlight if not correct
734 if (!is_light_locally_correct(map, ndef, bank,
735 v3s16(x, y, z) + b->getPosRelative())) {
736 // Initialize for unlighting
737 n.setLight(bank, 0, ndef);
738 b->setNodeNoCheck(x, y, z, n);
739 modified_blocks[b->getPos()]=b;
740 disappearing_lights.push(light,
741 relative_v3(x, y, z), b->getPos(), b,
748 // Remove lights
749 unspread_light(map, ndef, bank, disappearing_lights, light_sources,
750 modified_blocks);
751 // Initialize light values for light spreading.
752 for (u8 i = 0; i <= LIGHT_SUN; i++) {
753 const std::vector<ChangingLight> &lights = light_sources.lights[i];
754 for (std::vector<ChangingLight>::const_iterator it = lights.begin();
755 it < lights.end(); ++it) {
756 MapNode n = it->block->getNodeNoCheck(it->rel_position,
757 &is_valid_position);
758 n.setLight(bank, i, ndef);
759 it->block->setNodeNoCheck(it->rel_position, n);
762 // Spread lights.
763 spread_light(map, ndef, bank, light_sources, modified_blocks);
768 * Resets the lighting of the given VoxelManipulator to
769 * complete darkness and full sunlight.
770 * Operates in one map sector.
772 * \param offset contains the least x and z node coordinates
773 * of the map sector.
774 * \param light incoming sunlight, light[x][z] is true if there
775 * is sunlight above the voxel manipulator at the given x-z coordinates.
776 * The array's indices are relative node coordinates in the sector.
777 * After the procedure returns, this contains outgoing light at
778 * the bottom of the voxel manipulator.
780 void fill_with_sunlight(MMVManip *vm, const NodeDefManager *ndef, v2s16 offset,
781 bool light[MAP_BLOCKSIZE][MAP_BLOCKSIZE])
783 // Distance in array between two nodes on top of each other.
784 s16 ystride = vm->m_area.getExtent().X;
785 // Cache the ignore node.
786 MapNode ignore = MapNode(CONTENT_IGNORE);
787 // For each column of nodes:
788 for (s16 z = 0; z < MAP_BLOCKSIZE; z++)
789 for (s16 x = 0; x < MAP_BLOCKSIZE; x++) {
790 // Position of the column on the map.
791 v2s16 realpos = offset + v2s16(x, z);
792 // Array indices in the voxel manipulator
793 s32 maxindex = vm->m_area.index(realpos.X, vm->m_area.MaxEdge.Y,
794 realpos.Y);
795 s32 minindex = vm->m_area.index(realpos.X, vm->m_area.MinEdge.Y,
796 realpos.Y);
797 // True if the current node has sunlight.
798 bool lig = light[z][x];
799 // For each node, downwards:
800 for (s32 i = maxindex; i >= minindex; i -= ystride) {
801 MapNode *n;
802 if (vm->m_flags[i] & VOXELFLAG_NO_DATA)
803 n = &ignore;
804 else
805 n = &vm->m_data[i];
806 // Ignore IGNORE nodes, these are not generated yet.
807 if(n->getContent() == CONTENT_IGNORE)
808 continue;
809 const ContentFeatures &f = ndef->get(n->getContent());
810 if (lig && !f.sunlight_propagates)
811 // Sunlight is stopped.
812 lig = false;
813 // Reset light
814 n->setLight(LIGHTBANK_DAY, lig ? 15 : 0, f);
815 n->setLight(LIGHTBANK_NIGHT, 0, f);
817 // Output outgoing light.
818 light[z][x] = lig;
823 * Returns incoming sunlight for one map block.
824 * If block above is not found, it is loaded.
826 * \param pos position of the map block that gets the sunlight.
827 * \param light incoming sunlight, light[z][x] is true if there
828 * is sunlight above the block at the given z-x relative
829 * node coordinates.
831 void is_sunlight_above_block(ServerMap *map, mapblock_v3 pos,
832 const NodeDefManager *ndef, bool light[MAP_BLOCKSIZE][MAP_BLOCKSIZE])
834 mapblock_v3 source_block_pos = pos + v3s16(0, 1, 0);
835 // Get or load source block.
836 // It might take a while to load, but correcting incorrect
837 // sunlight may be even slower.
838 MapBlock *source_block = map->emergeBlock(source_block_pos, false);
839 // Trust only generated blocks.
840 if (source_block == NULL || source_block->isDummy()
841 || !source_block->isGenerated()) {
842 // But if there is no block above, then use heuristics
843 bool sunlight = true;
844 MapBlock *node_block = map->getBlockNoCreateNoEx(pos);
845 if (node_block == NULL)
846 // This should not happen.
847 sunlight = false;
848 else
849 sunlight = !node_block->getIsUnderground();
850 for (s16 z = 0; z < MAP_BLOCKSIZE; z++)
851 for (s16 x = 0; x < MAP_BLOCKSIZE; x++)
852 light[z][x] = sunlight;
853 } else {
854 // Dummy boolean, the position is valid.
855 bool is_valid_position;
856 // For each column:
857 for (s16 z = 0; z < MAP_BLOCKSIZE; z++)
858 for (s16 x = 0; x < MAP_BLOCKSIZE; x++) {
859 // Get the bottom block.
860 MapNode above = source_block->getNodeNoCheck(x, 0, z,
861 &is_valid_position);
862 light[z][x] = above.getLight(LIGHTBANK_DAY, ndef) == LIGHT_SUN;
868 * Propagates sunlight down in a given map block.
870 * \param data contains incoming sunlight and shadow and
871 * the coordinates of the target block.
872 * \param unlight propagated shadow is inserted here
873 * \param relight propagated sunlight is inserted here
875 * \returns true if the block was modified, false otherwise.
877 bool propagate_block_sunlight(Map *map, const NodeDefManager *ndef,
878 SunlightPropagationData *data, UnlightQueue *unlight, ReLightQueue *relight)
880 bool modified = false;
881 // Get the block.
882 MapBlock *block = map->getBlockNoCreateNoEx(data->target_block);
883 if (block == NULL || block->isDummy()) {
884 // The work is done if the block does not contain data.
885 data->data.clear();
886 return false;
888 // Dummy boolean
889 bool is_valid;
890 // For each changing column of nodes:
891 size_t index;
892 for (index = 0; index < data->data.size(); index++) {
893 SunlightPropagationUnit it = data->data[index];
894 // Relative position of the currently inspected node.
895 relative_v3 current_pos(it.relative_pos.X, MAP_BLOCKSIZE - 1,
896 it.relative_pos.Y);
897 if (it.is_sunlit) {
898 // Propagate sunlight.
899 // For each node downwards:
900 for (; current_pos.Y >= 0; current_pos.Y--) {
901 MapNode n = block->getNodeNoCheck(current_pos, &is_valid);
902 const ContentFeatures &f = ndef->get(n);
903 if (n.getLightRaw(LIGHTBANK_DAY, f) < LIGHT_SUN
904 && f.sunlight_propagates) {
905 // This node gets sunlight.
906 n.setLight(LIGHTBANK_DAY, LIGHT_SUN, f);
907 block->setNodeNoCheck(current_pos, n);
908 modified = true;
909 relight->push(LIGHT_SUN, current_pos, data->target_block,
910 block, 4);
911 } else {
912 // Light already valid, propagation stopped.
913 break;
916 } else {
917 // Propagate shadow.
918 // For each node downwards:
919 for (; current_pos.Y >= 0; current_pos.Y--) {
920 MapNode n = block->getNodeNoCheck(current_pos, &is_valid);
921 const ContentFeatures &f = ndef->get(n);
922 if (n.getLightRaw(LIGHTBANK_DAY, f) == LIGHT_SUN) {
923 // The sunlight is no longer valid.
924 n.setLight(LIGHTBANK_DAY, 0, f);
925 block->setNodeNoCheck(current_pos, n);
926 modified = true;
927 unlight->push(LIGHT_SUN, current_pos, data->target_block,
928 block, 4);
929 } else {
930 // Reached shadow, propagation stopped.
931 break;
935 if (current_pos.Y >= 0) {
936 // Propagation stopped, remove from data.
937 data->data[index] = data->data.back();
938 data->data.pop_back();
939 index--;
942 return modified;
946 * Borders of a map block in relative node coordinates.
947 * The areas do not overlap.
948 * Compatible with type 'direction'.
950 const VoxelArea block_pad[] = {
951 VoxelArea(v3s16(15, 0, 0), v3s16(15, 15, 15)), //X+
952 VoxelArea(v3s16(1, 15, 0), v3s16(14, 15, 15)), //Y+
953 VoxelArea(v3s16(1, 1, 15), v3s16(14, 14, 15)), //Z+
954 VoxelArea(v3s16(1, 1, 0), v3s16(14, 14, 0)), //Z-
955 VoxelArea(v3s16(1, 0, 0), v3s16(14, 0, 15)), //Y-
956 VoxelArea(v3s16(0, 0, 0), v3s16(0, 15, 15)) //X-
960 * The common part of bulk light updates - it is always executed.
961 * The procedure takes the nodes that should be unlit, and the
962 * full modified area.
964 * The procedure handles the correction of all lighting except
965 * direct sunlight spreading.
967 * \param minblock least coordinates of the changed area in block
968 * coordinates
969 * \param maxblock greatest coordinates of the changed area in block
970 * coordinates
971 * \param unlight the first queue is for day light, the second is for
972 * night light. Contains all nodes on the borders that need to be unlit.
973 * \param relight the first queue is for day light, the second is for
974 * night light. Contains nodes that were not modified, but got sunlight
975 * because the changes.
976 * \param modified_blocks the procedure adds all modified blocks to
977 * this map
979 void finish_bulk_light_update(Map *map, mapblock_v3 minblock,
980 mapblock_v3 maxblock, UnlightQueue unlight[2], ReLightQueue relight[2],
981 std::map<v3s16, MapBlock*> *modified_blocks)
983 const NodeDefManager *ndef = map->getNodeDefManager();
984 // dummy boolean
985 bool is_valid;
987 // --- STEP 1: Do unlighting
989 for (size_t bank = 0; bank < 2; bank++) {
990 LightBank b = banks[bank];
991 unspread_light(map, ndef, b, unlight[bank], relight[bank],
992 *modified_blocks);
995 // --- STEP 2: Get all newly inserted light sources
997 // For each block:
998 v3s16 blockpos;
999 v3s16 relpos;
1000 for (blockpos.X = minblock.X; blockpos.X <= maxblock.X; blockpos.X++)
1001 for (blockpos.Y = minblock.Y; blockpos.Y <= maxblock.Y; blockpos.Y++)
1002 for (blockpos.Z = minblock.Z; blockpos.Z <= maxblock.Z; blockpos.Z++) {
1003 MapBlock *block = map->getBlockNoCreateNoEx(blockpos);
1004 if (!block || block->isDummy())
1005 // Skip not existing blocks
1006 continue;
1007 // For each node in the block:
1008 for (relpos.X = 0; relpos.X < MAP_BLOCKSIZE; relpos.X++)
1009 for (relpos.Z = 0; relpos.Z < MAP_BLOCKSIZE; relpos.Z++)
1010 for (relpos.Y = 0; relpos.Y < MAP_BLOCKSIZE; relpos.Y++) {
1011 MapNode node = block->getNodeNoCheck(relpos.X, relpos.Y, relpos.Z, &is_valid);
1012 const ContentFeatures &f = ndef->get(node);
1014 // For each light bank
1015 for (size_t b = 0; b < 2; b++) {
1016 LightBank bank = banks[b];
1017 u8 light = f.param_type == CPT_LIGHT ?
1018 node.getLightNoChecks(bank, &f):
1019 f.light_source;
1020 if (light > 1)
1021 relight[b].push(light, relpos, blockpos, block, 6);
1022 } // end of banks
1023 } // end of nodes
1024 } // end of blocks
1026 // --- STEP 3: do light spreading
1028 // For each light bank:
1029 for (size_t b = 0; b < 2; b++) {
1030 LightBank bank = banks[b];
1031 // Sunlight is already initialized.
1032 u8 maxlight = (b == 0) ? LIGHT_MAX : LIGHT_SUN;
1033 // Initialize light values for light spreading.
1034 for (u8 i = 0; i <= maxlight; i++) {
1035 const std::vector<ChangingLight> &lights = relight[b].lights[i];
1036 for (std::vector<ChangingLight>::const_iterator it = lights.begin();
1037 it < lights.end(); ++it) {
1038 MapNode n = it->block->getNodeNoCheck(it->rel_position,
1039 &is_valid);
1040 n.setLight(bank, i, ndef);
1041 it->block->setNodeNoCheck(it->rel_position, n);
1044 // Spread lights.
1045 spread_light(map, ndef, bank, relight[b], *modified_blocks);
1049 void blit_back_with_light(ServerMap *map, MMVManip *vm,
1050 std::map<v3s16, MapBlock*> *modified_blocks)
1052 const NodeDefManager *ndef = map->getNodeDefManager();
1053 mapblock_v3 minblock = getNodeBlockPos(vm->m_area.MinEdge);
1054 mapblock_v3 maxblock = getNodeBlockPos(vm->m_area.MaxEdge);
1055 // First queue is for day light, second is for night light.
1056 UnlightQueue unlight[] = { UnlightQueue(256), UnlightQueue(256) };
1057 ReLightQueue relight[] = { ReLightQueue(256), ReLightQueue(256) };
1058 // Will hold sunlight data.
1059 bool lights[MAP_BLOCKSIZE][MAP_BLOCKSIZE];
1060 SunlightPropagationData data;
1061 // Dummy boolean.
1062 bool is_valid;
1064 // --- STEP 1: reset everything to sunlight
1066 // For each map block:
1067 for (s16 x = minblock.X; x <= maxblock.X; x++)
1068 for (s16 z = minblock.Z; z <= maxblock.Z; z++) {
1069 // Extract sunlight above.
1070 is_sunlight_above_block(map, v3s16(x, maxblock.Y, z), ndef, lights);
1071 v2s16 offset(x, z);
1072 offset *= MAP_BLOCKSIZE;
1073 // Reset the voxel manipulator.
1074 fill_with_sunlight(vm, ndef, offset, lights);
1075 // Copy sunlight data
1076 data.target_block = v3s16(x, minblock.Y - 1, z);
1077 for (s16 z = 0; z < MAP_BLOCKSIZE; z++)
1078 for (s16 x = 0; x < MAP_BLOCKSIZE; x++)
1079 data.data.emplace_back(v2s16(x, z), lights[z][x]);
1080 // Propagate sunlight and shadow below the voxel manipulator.
1081 while (!data.data.empty()) {
1082 if (propagate_block_sunlight(map, ndef, &data, &unlight[0],
1083 &relight[0]))
1084 (*modified_blocks)[data.target_block] =
1085 map->getBlockNoCreateNoEx(data.target_block);
1086 // Step downwards.
1087 data.target_block.Y--;
1091 // --- STEP 2: Get nodes from borders to unlight
1092 v3s16 blockpos;
1093 v3s16 relpos;
1095 // In case there are unloaded holes in the voxel manipulator
1096 // unlight each block.
1097 // For each block:
1098 for (blockpos.X = minblock.X; blockpos.X <= maxblock.X; blockpos.X++)
1099 for (blockpos.Y = minblock.Y; blockpos.Y <= maxblock.Y; blockpos.Y++)
1100 for (blockpos.Z = minblock.Z; blockpos.Z <= maxblock.Z; blockpos.Z++) {
1101 MapBlock *block = map->getBlockNoCreateNoEx(blockpos);
1102 if (!block || block->isDummy())
1103 // Skip not existing blocks.
1104 continue;
1105 v3s16 offset = block->getPosRelative();
1106 // For each border of the block:
1107 for (const VoxelArea &a : block_pad) {
1108 // For each node of the border:
1109 for (relpos.X = a.MinEdge.X; relpos.X <= a.MaxEdge.X; relpos.X++)
1110 for (relpos.Z = a.MinEdge.Z; relpos.Z <= a.MaxEdge.Z; relpos.Z++)
1111 for (relpos.Y = a.MinEdge.Y; relpos.Y <= a.MaxEdge.Y; relpos.Y++) {
1113 // Get old and new node
1114 MapNode oldnode = block->getNodeNoCheck(relpos, &is_valid);
1115 const ContentFeatures &oldf = ndef->get(oldnode);
1116 MapNode newnode = vm->getNodeNoExNoEmerge(relpos + offset);
1117 const ContentFeatures &newf = oldnode == newnode ? oldf :
1118 ndef->get(newnode);
1120 // For each light bank
1121 for (size_t b = 0; b < 2; b++) {
1122 LightBank bank = banks[b];
1123 u8 oldlight = oldf.param_type == CPT_LIGHT ?
1124 oldnode.getLightNoChecks(bank, &oldf):
1125 LIGHT_SUN; // no light information, force unlighting
1126 u8 newlight = newf.param_type == CPT_LIGHT ?
1127 newnode.getLightNoChecks(bank, &newf):
1128 newf.light_source;
1129 // If the new node is dimmer, unlight.
1130 if (oldlight > newlight) {
1131 unlight[b].push(
1132 oldlight, relpos, blockpos, block, 6);
1134 } // end of banks
1135 } // end of nodes
1136 } // end of borders
1137 } // end of blocks
1139 // --- STEP 3: All information extracted, overwrite
1141 vm->blitBackAll(modified_blocks, true);
1143 // --- STEP 4: Finish light update
1145 finish_bulk_light_update(map, minblock, maxblock, unlight, relight,
1146 modified_blocks);
1150 * Resets the lighting of the given map block to
1151 * complete darkness and full sunlight.
1153 * \param light incoming sunlight, light[x][z] is true if there
1154 * is sunlight above the map block at the given x-z coordinates.
1155 * The array's indices are relative node coordinates in the block.
1156 * After the procedure returns, this contains outgoing light at
1157 * the bottom of the map block.
1159 void fill_with_sunlight(MapBlock *block, const NodeDefManager *ndef,
1160 bool light[MAP_BLOCKSIZE][MAP_BLOCKSIZE])
1162 if (block->isDummy())
1163 return;
1164 // dummy boolean
1165 bool is_valid;
1166 // For each column of nodes:
1167 for (s16 z = 0; z < MAP_BLOCKSIZE; z++)
1168 for (s16 x = 0; x < MAP_BLOCKSIZE; x++) {
1169 // True if the current node has sunlight.
1170 bool lig = light[z][x];
1171 // For each node, downwards:
1172 for (s16 y = MAP_BLOCKSIZE - 1; y >= 0; y--) {
1173 MapNode n = block->getNodeNoCheck(x, y, z, &is_valid);
1174 // Ignore IGNORE nodes, these are not generated yet.
1175 if (n.getContent() == CONTENT_IGNORE)
1176 continue;
1177 const ContentFeatures &f = ndef->get(n.getContent());
1178 if (lig && !f.sunlight_propagates) {
1179 // Sunlight is stopped.
1180 lig = false;
1182 // Reset light
1183 n.setLight(LIGHTBANK_DAY, lig ? 15 : 0, f);
1184 n.setLight(LIGHTBANK_NIGHT, 0, f);
1185 block->setNodeNoCheck(x, y, z, n);
1187 // Output outgoing light.
1188 light[z][x] = lig;
1192 void repair_block_light(ServerMap *map, MapBlock *block,
1193 std::map<v3s16, MapBlock*> *modified_blocks)
1195 if (!block || block->isDummy())
1196 return;
1197 const NodeDefManager *ndef = map->getNodeDefManager();
1198 // First queue is for day light, second is for night light.
1199 UnlightQueue unlight[] = { UnlightQueue(256), UnlightQueue(256) };
1200 ReLightQueue relight[] = { ReLightQueue(256), ReLightQueue(256) };
1201 // Will hold sunlight data.
1202 bool lights[MAP_BLOCKSIZE][MAP_BLOCKSIZE];
1203 SunlightPropagationData data;
1204 // Dummy boolean.
1205 bool is_valid;
1207 // --- STEP 1: reset everything to sunlight
1209 mapblock_v3 blockpos = block->getPos();
1210 (*modified_blocks)[blockpos] = block;
1211 // For each map block:
1212 // Extract sunlight above.
1213 is_sunlight_above_block(map, blockpos, ndef, lights);
1214 // Reset the voxel manipulator.
1215 fill_with_sunlight(block, ndef, lights);
1216 // Copy sunlight data
1217 data.target_block = v3s16(blockpos.X, blockpos.Y - 1, blockpos.Z);
1218 for (s16 z = 0; z < MAP_BLOCKSIZE; z++)
1219 for (s16 x = 0; x < MAP_BLOCKSIZE; x++) {
1220 data.data.emplace_back(v2s16(x, z), lights[z][x]);
1222 // Propagate sunlight and shadow below the voxel manipulator.
1223 while (!data.data.empty()) {
1224 if (propagate_block_sunlight(map, ndef, &data, &unlight[0],
1225 &relight[0]))
1226 (*modified_blocks)[data.target_block] =
1227 map->getBlockNoCreateNoEx(data.target_block);
1228 // Step downwards.
1229 data.target_block.Y--;
1232 // --- STEP 2: Get nodes from borders to unlight
1234 // For each border of the block:
1235 for (const VoxelArea &a : block_pad) {
1236 v3s16 relpos;
1237 // For each node of the border:
1238 for (relpos.X = a.MinEdge.X; relpos.X <= a.MaxEdge.X; relpos.X++)
1239 for (relpos.Z = a.MinEdge.Z; relpos.Z <= a.MaxEdge.Z; relpos.Z++)
1240 for (relpos.Y = a.MinEdge.Y; relpos.Y <= a.MaxEdge.Y; relpos.Y++) {
1242 // Get node
1243 MapNode node = block->getNodeNoCheck(relpos, &is_valid);
1244 const ContentFeatures &f = ndef->get(node);
1245 // For each light bank
1246 for (size_t b = 0; b < 2; b++) {
1247 LightBank bank = banks[b];
1248 u8 light = f.param_type == CPT_LIGHT ?
1249 node.getLightNoChecks(bank, &f):
1250 f.light_source;
1251 // If the new node is dimmer than sunlight, unlight.
1252 // (if it has maximal light, it is pointless to remove
1253 // surrounding light, as it can only become brighter)
1254 if (LIGHT_SUN > light) {
1255 unlight[b].push(
1256 LIGHT_SUN, relpos, blockpos, block, 6);
1258 } // end of banks
1259 } // end of nodes
1260 } // end of borders
1262 // STEP 3: Remove and spread light
1264 finish_bulk_light_update(map, blockpos, blockpos, unlight, relight,
1265 modified_blocks);
1268 VoxelLineIterator::VoxelLineIterator(const v3f &start_position, const v3f &line_vector) :
1269 m_start_position(start_position),
1270 m_line_vector(line_vector)
1272 m_current_node_pos = floatToInt(m_start_position, 1);
1273 m_start_node_pos = m_current_node_pos;
1274 m_last_index = getIndex(floatToInt(start_position + line_vector, 1));
1276 if (m_line_vector.X > 0) {
1277 m_next_intersection_multi.X = (floorf(m_start_position.X - 0.5) + 1.5
1278 - m_start_position.X) / m_line_vector.X;
1279 m_intersection_multi_inc.X = 1 / m_line_vector.X;
1280 } else if (m_line_vector.X < 0) {
1281 m_next_intersection_multi.X = (floorf(m_start_position.X - 0.5)
1282 - m_start_position.X + 0.5) / m_line_vector.X;
1283 m_intersection_multi_inc.X = -1 / m_line_vector.X;
1284 m_step_directions.X = -1;
1287 if (m_line_vector.Y > 0) {
1288 m_next_intersection_multi.Y = (floorf(m_start_position.Y - 0.5) + 1.5
1289 - m_start_position.Y) / m_line_vector.Y;
1290 m_intersection_multi_inc.Y = 1 / m_line_vector.Y;
1291 } else if (m_line_vector.Y < 0) {
1292 m_next_intersection_multi.Y = (floorf(m_start_position.Y - 0.5)
1293 - m_start_position.Y + 0.5) / m_line_vector.Y;
1294 m_intersection_multi_inc.Y = -1 / m_line_vector.Y;
1295 m_step_directions.Y = -1;
1298 if (m_line_vector.Z > 0) {
1299 m_next_intersection_multi.Z = (floorf(m_start_position.Z - 0.5) + 1.5
1300 - m_start_position.Z) / m_line_vector.Z;
1301 m_intersection_multi_inc.Z = 1 / m_line_vector.Z;
1302 } else if (m_line_vector.Z < 0) {
1303 m_next_intersection_multi.Z = (floorf(m_start_position.Z - 0.5)
1304 - m_start_position.Z + 0.5) / m_line_vector.Z;
1305 m_intersection_multi_inc.Z = -1 / m_line_vector.Z;
1306 m_step_directions.Z = -1;
1310 void VoxelLineIterator::next()
1312 m_current_index++;
1313 if ((m_next_intersection_multi.X < m_next_intersection_multi.Y)
1314 && (m_next_intersection_multi.X < m_next_intersection_multi.Z)) {
1315 m_next_intersection_multi.X += m_intersection_multi_inc.X;
1316 m_current_node_pos.X += m_step_directions.X;
1317 } else if ((m_next_intersection_multi.Y < m_next_intersection_multi.Z)) {
1318 m_next_intersection_multi.Y += m_intersection_multi_inc.Y;
1319 m_current_node_pos.Y += m_step_directions.Y;
1320 } else {
1321 m_next_intersection_multi.Z += m_intersection_multi_inc.Z;
1322 m_current_node_pos.Z += m_step_directions.Z;
1326 s16 VoxelLineIterator::getIndex(v3s16 voxel){
1327 return
1328 abs(voxel.X - m_start_node_pos.X) +
1329 abs(voxel.Y - m_start_node_pos.Y) +
1330 abs(voxel.Z - m_start_node_pos.Z);
1333 } // namespace voxalgo