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(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
),
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
, 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
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
;
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
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
++) {
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) {
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);
294 neighbor_block
= current
.block
;
296 // Get the neighbor itself
297 MapNode neighbor
= neighbor_block
->getNodeNoCheck(neighbor_rel_pos
,
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
;
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
;
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.
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
;
365 bool is_valid_position
;
366 while (light_sources
.next(spreading_light
, current
)) {
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) {
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);
384 neighbor_block
= current
.block
;
386 // Get the neighbor itself
387 MapNode neighbor
= neighbor_block
->getNodeNoCheck(neighbor_rel_pos
,
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
{
413 SunlightPropagationUnit(v2s16 relpos
, bool sunlit
):
414 relative_pos(relpos
),
419 struct SunlightPropagationData
{
420 std::vector
<SunlightPropagationUnit
> data
;
425 * Returns true if the node gets sunlight from the
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
,
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
) {
445 sunlight
= !node_block
->getIsUnderground();
448 bool is_valid_position
;
449 MapNode above
= source_block
->getNodeNoCheck(source_rel_pos
,
451 if (is_valid_position
) {
452 if (above
.getContent() == CONTENT_IGNORE
) {
454 if (source_block
->getIsUnderground()) {
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.
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
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) {
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
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()) {
510 MapNode n
= block
->getNodeNoCheck(rel_pos
, &is_valid_position
);
511 if (!is_valid_position
) {
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
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
;
528 new_light
= ndef
->get(n
).light_source
;
529 for (const v3s16
&neighbor_dir
: neighbor_dirs
) {
530 v3s16 p2
= p
+ neighbor_dir
;
532 MapNode n2
= map
->getNode(p2
, &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;
544 // If this is an opaque node, it still can emit light.
545 new_light
= ndef
->get(n
).light_source
;
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
);
569 n2
= map
->getNode(n2pos
, &is_valid_position
);
570 if (!is_valid_position
)
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
) {
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(
586 disappearing_lights
.push(LIGHT_SUN
, rel_pos2
,
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
);
601 n2
= map
->getNode(n2pos
, &is_valid_position
);
602 if (!is_valid_position
)
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
) {
610 // If the node terminates sunlight, stop.
611 if (!ndef
->get(n2
).sunlight_propagates
) {
614 relative_v3 rel_pos2
;
615 mapblock_v3 block_pos2
;
616 getNodeBlockPosWithOffset(n2pos
, block_pos2
, rel_pos2
);
617 MapBlock
*block2
= map
->getBlockNoCreateNoEx(
619 // Mark node for lighting.
620 light_sources
.push(LIGHT_SUN
, rel_pos2
, block_pos2
,
628 unspread_light(map
, ndef
, bank
, disappearing_lights
, light_sources
,
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
,
637 n
.setLight(bank
, i
, ndef
);
638 it
->block
->setNodeNoCheck(it
->rel_position
, n
);
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-
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
) {
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
,
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
);
710 // Only update if lighting was not completed.
711 if (block
->isLightingComplete(bank
, d
) &&
712 other
->isLightingComplete(bank
, 5 - d
))
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
]};
721 for (u8 blocknum
= 0; blocknum
< 2; blocknum
++) {
722 MapBlock
*b
= blocks
[blocknum
];
723 VoxelArea a
= areas
[blocknum
];
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
,
730 u8 light
= n
.getLight(bank
, ndef
);
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
,
749 unspread_light(map
, ndef
, bank
, disappearing_lights
, light_sources
,
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
,
758 n
.setLight(bank
, i
, ndef
);
759 it
->block
->setNodeNoCheck(it
->rel_position
, n
);
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
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
,
795 s32 minindex
= vm
->m_area
.index(realpos
.X
, vm
->m_area
.MinEdge
.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
) {
802 if (vm
->m_flags
[i
] & VOXELFLAG_NO_DATA
)
806 // Ignore IGNORE nodes, these are not generated yet.
807 if(n
->getContent() == CONTENT_IGNORE
)
809 const ContentFeatures
&f
= ndef
->get(n
->getContent());
810 if (lig
&& !f
.sunlight_propagates
)
811 // Sunlight is stopped.
814 n
->setLight(LIGHTBANK_DAY
, lig
? 15 : 0, f
);
815 n
->setLight(LIGHTBANK_NIGHT
, 0, f
);
817 // Output outgoing light.
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
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.
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
;
854 // Dummy boolean, the position is valid.
855 bool is_valid_position
;
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
,
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;
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.
890 // For each changing column of nodes:
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,
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
);
909 relight
->push(LIGHT_SUN
, current_pos
, data
->target_block
,
912 // Light already valid, propagation stopped.
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
);
927 unlight
->push(LIGHT_SUN
, current_pos
, data
->target_block
,
930 // Reached shadow, propagation stopped.
935 if (current_pos
.Y
>= 0) {
936 // Propagation stopped, remove from data.
937 data
->data
[index
] = data
->data
.back();
938 data
->data
.pop_back();
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
969 * \param maxblock greatest coordinates of the changed area in block
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
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();
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
],
995 // --- STEP 2: Get all newly inserted light sources
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
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
):
1021 relight
[b
].push(light
, relpos
, blockpos
, block
, 6);
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
,
1040 n
.setLight(bank
, i
, ndef
);
1041 it
->block
->setNodeNoCheck(it
->rel_position
, n
);
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
;
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
);
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],
1084 (*modified_blocks
)[data
.target_block
] =
1085 map
->getBlockNoCreateNoEx(data
.target_block
);
1087 data
.target_block
.Y
--;
1091 // --- STEP 2: Get nodes from borders to unlight
1095 // In case there are unloaded holes in the voxel manipulator
1096 // unlight 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.
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
:
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
):
1129 // If the new node is dimmer, unlight.
1130 if (oldlight
> newlight
) {
1132 oldlight
, relpos
, blockpos
, block
, 6);
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
,
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())
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
)
1177 const ContentFeatures
&f
= ndef
->get(n
.getContent());
1178 if (lig
&& !f
.sunlight_propagates
) {
1179 // Sunlight is stopped.
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.
1192 void repair_block_light(ServerMap
*map
, MapBlock
*block
,
1193 std::map
<v3s16
, MapBlock
*> *modified_blocks
)
1195 if (!block
|| block
->isDummy())
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
;
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],
1226 (*modified_blocks
)[data
.target_block
] =
1227 map
->getBlockNoCreateNoEx(data
.target_block
);
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
) {
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
++) {
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
):
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
) {
1256 LIGHT_SUN
, relpos
, blockpos
, block
, 6);
1262 // STEP 3: Remove and spread light
1264 finish_bulk_light_update(map
, blockpos
, blockpos
, unlight
, relight
,
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()
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
;
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
){
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