forked from Mirrorlandia_minetest/minetest
Light calculation: New bulk node lighting code
This commit introduces a new bulk node lighting algorithm to minimize lighting bugs during l-system tree generation, schematic placement and non-mapgen-object lua voxelmanip light calculation. If the block above the changed area is not loaded, it gets loaded to avoid lighting bugs. Light is updated as soon as write_to_map is called on a voxel manipulator, therefore update_map does nothing.
This commit is contained in:
parent
d785456b3f
commit
ab371cc934
@ -3282,9 +3282,6 @@ format as produced by get_data() et al. and is *not required* to be a table retr
|
|||||||
Once the internal VoxelManip state has been modified to your liking, the changes can be committed back
|
Once the internal VoxelManip state has been modified to your liking, the changes can be committed back
|
||||||
to the map by calling `VoxelManip:write_to_map()`.
|
to the map by calling `VoxelManip:write_to_map()`.
|
||||||
|
|
||||||
Finally, a call to `VoxelManip:update_map()` is required to re-calculate lighting and set the blocks
|
|
||||||
as being modified so that connected clients are sent the updated parts of map.
|
|
||||||
|
|
||||||
|
|
||||||
##### Flat array format
|
##### Flat array format
|
||||||
Let
|
Let
|
||||||
@ -3349,8 +3346,6 @@ but with a few differences:
|
|||||||
will also update the Mapgen VoxelManip object's internal state active on the current thread.
|
will also update the Mapgen VoxelManip object's internal state active on the current thread.
|
||||||
* After modifying the Mapgen VoxelManip object's internal buffer, it may be necessary to update lighting
|
* After modifying the Mapgen VoxelManip object's internal buffer, it may be necessary to update lighting
|
||||||
information using either: `VoxelManip:calc_lighting()` or `VoxelManip:set_lighting()`.
|
information using either: `VoxelManip:calc_lighting()` or `VoxelManip:set_lighting()`.
|
||||||
* `VoxelManip:update_map()` does not need to be called after `write_to_map()`. The map update is performed
|
|
||||||
automatically after all on_generated callbacks have been run for that generated block.
|
|
||||||
|
|
||||||
##### Other API functions operating on a VoxelManip
|
##### Other API functions operating on a VoxelManip
|
||||||
If any VoxelManip contents were set to a liquid node, `VoxelManip:update_liquids()` must be called
|
If any VoxelManip contents were set to a liquid node, `VoxelManip:update_liquids()` must be called
|
||||||
@ -3393,9 +3388,7 @@ will place the schematic inside of the VoxelManip.
|
|||||||
* returns raw node data in the form of an array of node content IDs
|
* returns raw node data in the form of an array of node content IDs
|
||||||
* if the param `buffer` is present, this table will be used to store the result instead
|
* if the param `buffer` is present, this table will be used to store the result instead
|
||||||
* `set_data(data)`: Sets the data contents of the `VoxelManip` object
|
* `set_data(data)`: Sets the data contents of the `VoxelManip` object
|
||||||
* `update_map()`: Update map after writing chunk back to map.
|
* `update_map()`: Does nothing, kept for compatibility.
|
||||||
* To be used only by `VoxelManip` objects created by the mod itself;
|
|
||||||
not a `VoxelManip` that was retrieved from `minetest.get_mapgen_object`
|
|
||||||
* `set_lighting(light, [p1, p2])`: Set the lighting within the `VoxelManip` to a uniform value
|
* `set_lighting(light, [p1, p2])`: Set the lighting within the `VoxelManip` to a uniform value
|
||||||
* `light` is a table, `{day=<0...15>, night=<0...15>}`
|
* `light` is a table, `{day=<0...15>, night=<0...15>}`
|
||||||
* To be used only by a `VoxelManip` object from `minetest.get_mapgen_object`
|
* To be used only by a `VoxelManip` object from `minetest.get_mapgen_object`
|
||||||
|
565
src/map.cpp
565
src/map.cpp
@ -235,571 +235,6 @@ void Map::setNode(v3s16 p, MapNode & n)
|
|||||||
block->setNodeNoCheck(relpos, n);
|
block->setNodeNoCheck(relpos, n);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
Goes recursively through the neighbours of the node.
|
|
||||||
|
|
||||||
Alters only transparent nodes.
|
|
||||||
|
|
||||||
If the lighting of the neighbour is lower than the lighting of
|
|
||||||
the node was (before changing it to 0 at the step before), the
|
|
||||||
lighting of the neighbour is set to 0 and then the same stuff
|
|
||||||
repeats for the neighbour.
|
|
||||||
|
|
||||||
The ending nodes of the routine are stored in light_sources.
|
|
||||||
This is useful when a light is removed. In such case, this
|
|
||||||
routine can be called for the light node and then again for
|
|
||||||
light_sources to re-light the area without the removed light.
|
|
||||||
|
|
||||||
values of from_nodes are lighting values.
|
|
||||||
*/
|
|
||||||
void Map::unspreadLight(enum LightBank bank,
|
|
||||||
std::map<v3s16, u8> & from_nodes,
|
|
||||||
std::set<v3s16> & light_sources,
|
|
||||||
std::map<v3s16, MapBlock*> & modified_blocks)
|
|
||||||
{
|
|
||||||
v3s16 dirs[6] = {
|
|
||||||
v3s16(0,0,1), // back
|
|
||||||
v3s16(0,1,0), // top
|
|
||||||
v3s16(1,0,0), // right
|
|
||||||
v3s16(0,0,-1), // front
|
|
||||||
v3s16(0,-1,0), // bottom
|
|
||||||
v3s16(-1,0,0), // left
|
|
||||||
};
|
|
||||||
|
|
||||||
if(from_nodes.empty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
u32 blockchangecount = 0;
|
|
||||||
|
|
||||||
std::map<v3s16, u8> unlighted_nodes;
|
|
||||||
|
|
||||||
/*
|
|
||||||
Initialize block cache
|
|
||||||
*/
|
|
||||||
v3s16 blockpos_last;
|
|
||||||
MapBlock *block = NULL;
|
|
||||||
// Cache this a bit, too
|
|
||||||
bool block_checked_in_modified = false;
|
|
||||||
|
|
||||||
for(std::map<v3s16, u8>::iterator j = from_nodes.begin();
|
|
||||||
j != from_nodes.end(); ++j)
|
|
||||||
{
|
|
||||||
v3s16 pos = j->first;
|
|
||||||
v3s16 blockpos = getNodeBlockPos(pos);
|
|
||||||
|
|
||||||
// Only fetch a new block if the block position has changed
|
|
||||||
try{
|
|
||||||
if(block == NULL || blockpos != blockpos_last){
|
|
||||||
block = getBlockNoCreate(blockpos);
|
|
||||||
blockpos_last = blockpos;
|
|
||||||
|
|
||||||
block_checked_in_modified = false;
|
|
||||||
blockchangecount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch(InvalidPositionException &e)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(block->isDummy())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Calculate relative position in block
|
|
||||||
//v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
|
|
||||||
|
|
||||||
// Get node straight from the block
|
|
||||||
//MapNode n = block->getNode(relpos);
|
|
||||||
|
|
||||||
u8 oldlight = j->second;
|
|
||||||
|
|
||||||
// Loop through 6 neighbors
|
|
||||||
for(u16 i=0; i<6; i++)
|
|
||||||
{
|
|
||||||
// Get the position of the neighbor node
|
|
||||||
v3s16 n2pos = pos + dirs[i];
|
|
||||||
|
|
||||||
// Get the block where the node is located
|
|
||||||
v3s16 blockpos, relpos;
|
|
||||||
getNodeBlockPosWithOffset(n2pos, blockpos, relpos);
|
|
||||||
|
|
||||||
// Only fetch a new block if the block position has changed
|
|
||||||
try {
|
|
||||||
if(block == NULL || blockpos != blockpos_last){
|
|
||||||
block = getBlockNoCreate(blockpos);
|
|
||||||
blockpos_last = blockpos;
|
|
||||||
|
|
||||||
block_checked_in_modified = false;
|
|
||||||
blockchangecount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch(InvalidPositionException &e) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get node straight from the block
|
|
||||||
bool is_valid_position;
|
|
||||||
MapNode n2 = block->getNode(relpos, &is_valid_position);
|
|
||||||
if (!is_valid_position)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
bool changed = false;
|
|
||||||
|
|
||||||
//TODO: Optimize output by optimizing light_sources?
|
|
||||||
|
|
||||||
/*
|
|
||||||
If the neighbor is dimmer than what was specified
|
|
||||||
as oldlight (the light of the previous node)
|
|
||||||
*/
|
|
||||||
if(n2.getLight(bank, m_nodedef) < oldlight)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
And the neighbor is transparent and it has some light
|
|
||||||
*/
|
|
||||||
if(m_nodedef->get(n2).light_propagates
|
|
||||||
&& n2.getLight(bank, m_nodedef) != 0)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
Set light to 0 and add to queue
|
|
||||||
*/
|
|
||||||
|
|
||||||
u8 current_light = n2.getLight(bank, m_nodedef);
|
|
||||||
n2.setLight(bank, 0, m_nodedef);
|
|
||||||
block->setNode(relpos, n2);
|
|
||||||
|
|
||||||
unlighted_nodes[n2pos] = current_light;
|
|
||||||
changed = true;
|
|
||||||
|
|
||||||
/*
|
|
||||||
Remove from light_sources if it is there
|
|
||||||
NOTE: This doesn't happen nearly at all
|
|
||||||
*/
|
|
||||||
/*if(light_sources.find(n2pos))
|
|
||||||
{
|
|
||||||
infostream<<"Removed from light_sources"<<std::endl;
|
|
||||||
light_sources.remove(n2pos);
|
|
||||||
}*/
|
|
||||||
}
|
|
||||||
|
|
||||||
/*// DEBUG
|
|
||||||
if(light_sources.find(n2pos) != NULL)
|
|
||||||
light_sources.remove(n2pos);*/
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
light_sources.insert(n2pos);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add to modified_blocks
|
|
||||||
if(changed == true && block_checked_in_modified == false)
|
|
||||||
{
|
|
||||||
// If the block is not found in modified_blocks, add.
|
|
||||||
if(modified_blocks.find(blockpos) == modified_blocks.end())
|
|
||||||
{
|
|
||||||
modified_blocks[blockpos] = block;
|
|
||||||
}
|
|
||||||
block_checked_in_modified = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*infostream<<"unspreadLight(): Changed block "
|
|
||||||
<<blockchangecount<<" times"
|
|
||||||
<<" for "<<from_nodes.size()<<" nodes"
|
|
||||||
<<std::endl;*/
|
|
||||||
|
|
||||||
if(!unlighted_nodes.empty())
|
|
||||||
unspreadLight(bank, unlighted_nodes, light_sources, modified_blocks);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Lights neighbors of from_nodes, collects all them and then
|
|
||||||
goes on recursively.
|
|
||||||
*/
|
|
||||||
void Map::spreadLight(enum LightBank bank,
|
|
||||||
std::set<v3s16> & from_nodes,
|
|
||||||
std::map<v3s16, MapBlock*> & modified_blocks)
|
|
||||||
{
|
|
||||||
const v3s16 dirs[6] = {
|
|
||||||
v3s16(0,0,1), // back
|
|
||||||
v3s16(0,1,0), // top
|
|
||||||
v3s16(1,0,0), // right
|
|
||||||
v3s16(0,0,-1), // front
|
|
||||||
v3s16(0,-1,0), // bottom
|
|
||||||
v3s16(-1,0,0), // left
|
|
||||||
};
|
|
||||||
|
|
||||||
if(from_nodes.empty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
u32 blockchangecount = 0;
|
|
||||||
|
|
||||||
std::set<v3s16> lighted_nodes;
|
|
||||||
|
|
||||||
/*
|
|
||||||
Initialize block cache
|
|
||||||
*/
|
|
||||||
v3s16 blockpos_last;
|
|
||||||
MapBlock *block = NULL;
|
|
||||||
// Cache this a bit, too
|
|
||||||
bool block_checked_in_modified = false;
|
|
||||||
|
|
||||||
for(std::set<v3s16>::iterator j = from_nodes.begin();
|
|
||||||
j != from_nodes.end(); ++j)
|
|
||||||
{
|
|
||||||
v3s16 pos = *j;
|
|
||||||
v3s16 blockpos, relpos;
|
|
||||||
|
|
||||||
getNodeBlockPosWithOffset(pos, blockpos, relpos);
|
|
||||||
|
|
||||||
// Only fetch a new block if the block position has changed
|
|
||||||
try {
|
|
||||||
if(block == NULL || blockpos != blockpos_last){
|
|
||||||
block = getBlockNoCreate(blockpos);
|
|
||||||
blockpos_last = blockpos;
|
|
||||||
|
|
||||||
block_checked_in_modified = false;
|
|
||||||
blockchangecount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch(InvalidPositionException &e) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(block->isDummy())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Get node straight from the block
|
|
||||||
bool is_valid_position;
|
|
||||||
MapNode n = block->getNode(relpos, &is_valid_position);
|
|
||||||
|
|
||||||
u8 oldlight = is_valid_position ? n.getLight(bank, m_nodedef) : 0;
|
|
||||||
u8 newlight = diminish_light(oldlight);
|
|
||||||
|
|
||||||
// Loop through 6 neighbors
|
|
||||||
for(u16 i=0; i<6; i++){
|
|
||||||
// Get the position of the neighbor node
|
|
||||||
v3s16 n2pos = pos + dirs[i];
|
|
||||||
|
|
||||||
// Get the block where the node is located
|
|
||||||
v3s16 blockpos, relpos;
|
|
||||||
getNodeBlockPosWithOffset(n2pos, blockpos, relpos);
|
|
||||||
|
|
||||||
// Only fetch a new block if the block position has changed
|
|
||||||
try {
|
|
||||||
if(block == NULL || blockpos != blockpos_last){
|
|
||||||
block = getBlockNoCreate(blockpos);
|
|
||||||
blockpos_last = blockpos;
|
|
||||||
|
|
||||||
block_checked_in_modified = false;
|
|
||||||
blockchangecount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch(InvalidPositionException &e) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get node straight from the block
|
|
||||||
MapNode n2 = block->getNode(relpos, &is_valid_position);
|
|
||||||
if (!is_valid_position)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
bool changed = false;
|
|
||||||
/*
|
|
||||||
If the neighbor is brighter than the current node,
|
|
||||||
add to list (it will light up this node on its turn)
|
|
||||||
*/
|
|
||||||
if(n2.getLight(bank, m_nodedef) > undiminish_light(oldlight))
|
|
||||||
{
|
|
||||||
lighted_nodes.insert(n2pos);
|
|
||||||
changed = true;
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
If the neighbor is dimmer than how much light this node
|
|
||||||
would spread on it, add to list
|
|
||||||
*/
|
|
||||||
if(n2.getLight(bank, m_nodedef) < newlight)
|
|
||||||
{
|
|
||||||
if(m_nodedef->get(n2).light_propagates)
|
|
||||||
{
|
|
||||||
n2.setLight(bank, newlight, m_nodedef);
|
|
||||||
block->setNode(relpos, n2);
|
|
||||||
lighted_nodes.insert(n2pos);
|
|
||||||
changed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add to modified_blocks
|
|
||||||
if(changed == true && block_checked_in_modified == false)
|
|
||||||
{
|
|
||||||
// If the block is not found in modified_blocks, add.
|
|
||||||
if(modified_blocks.find(blockpos) == modified_blocks.end())
|
|
||||||
{
|
|
||||||
modified_blocks[blockpos] = block;
|
|
||||||
}
|
|
||||||
block_checked_in_modified = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*infostream<<"spreadLight(): Changed block "
|
|
||||||
<<blockchangecount<<" times"
|
|
||||||
<<" for "<<from_nodes.size()<<" nodes"
|
|
||||||
<<std::endl;*/
|
|
||||||
|
|
||||||
if(!lighted_nodes.empty())
|
|
||||||
spreadLight(bank, lighted_nodes, modified_blocks);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Map::updateLighting(enum LightBank bank,
|
|
||||||
std::map<v3s16, MapBlock*> & a_blocks,
|
|
||||||
std::map<v3s16, MapBlock*> & modified_blocks)
|
|
||||||
{
|
|
||||||
/*m_dout<<"Map::updateLighting(): "
|
|
||||||
<<a_blocks.size()<<" blocks."<<std::endl;*/
|
|
||||||
|
|
||||||
//TimeTaker timer("updateLighting");
|
|
||||||
|
|
||||||
// For debugging
|
|
||||||
//bool debug=true;
|
|
||||||
//u32 count_was = modified_blocks.size();
|
|
||||||
|
|
||||||
//std::map<v3s16, MapBlock*> blocks_to_update;
|
|
||||||
|
|
||||||
std::set<v3s16> light_sources;
|
|
||||||
|
|
||||||
std::map<v3s16, u8> unlight_from;
|
|
||||||
|
|
||||||
int num_bottom_invalid = 0;
|
|
||||||
|
|
||||||
{
|
|
||||||
//TimeTaker t("first stuff");
|
|
||||||
|
|
||||||
for(std::map<v3s16, MapBlock*>::iterator i = a_blocks.begin();
|
|
||||||
i != a_blocks.end(); ++i)
|
|
||||||
{
|
|
||||||
MapBlock *block = i->second;
|
|
||||||
|
|
||||||
for(;;)
|
|
||||||
{
|
|
||||||
// Don't bother with dummy blocks.
|
|
||||||
if(block->isDummy())
|
|
||||||
break;
|
|
||||||
|
|
||||||
v3s16 pos = block->getPos();
|
|
||||||
v3s16 posnodes = block->getPosRelative();
|
|
||||||
modified_blocks[pos] = block;
|
|
||||||
//blocks_to_update[pos] = block;
|
|
||||||
|
|
||||||
/*
|
|
||||||
Clear all light from block
|
|
||||||
*/
|
|
||||||
for(s16 z=0; z<MAP_BLOCKSIZE; z++)
|
|
||||||
for(s16 x=0; x<MAP_BLOCKSIZE; x++)
|
|
||||||
for(s16 y=0; y<MAP_BLOCKSIZE; y++)
|
|
||||||
{
|
|
||||||
v3s16 p(x,y,z);
|
|
||||||
bool is_valid_position;
|
|
||||||
MapNode n = block->getNode(p, &is_valid_position);
|
|
||||||
if (!is_valid_position) {
|
|
||||||
/* This would happen when dealing with a
|
|
||||||
dummy block.
|
|
||||||
*/
|
|
||||||
infostream<<"updateLighting(): InvalidPositionException"
|
|
||||||
<<std::endl;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
u8 oldlight = n.getLight(bank, m_nodedef);
|
|
||||||
n.setLight(bank, 0, m_nodedef);
|
|
||||||
block->setNode(p, n);
|
|
||||||
|
|
||||||
// If node sources light, add to list
|
|
||||||
u8 source = m_nodedef->get(n).light_source;
|
|
||||||
if(source != 0)
|
|
||||||
light_sources.insert(p + posnodes);
|
|
||||||
|
|
||||||
// Collect borders for unlighting
|
|
||||||
if((x==0 || x == MAP_BLOCKSIZE-1
|
|
||||||
|| y==0 || y == MAP_BLOCKSIZE-1
|
|
||||||
|| z==0 || z == MAP_BLOCKSIZE-1)
|
|
||||||
&& oldlight != 0)
|
|
||||||
{
|
|
||||||
v3s16 p_map = p + posnodes;
|
|
||||||
unlight_from[p_map] = oldlight;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if(bank == LIGHTBANK_DAY)
|
|
||||||
{
|
|
||||||
bool bottom_valid = block->propagateSunlight(light_sources);
|
|
||||||
|
|
||||||
if(!bottom_valid)
|
|
||||||
num_bottom_invalid++;
|
|
||||||
|
|
||||||
// If bottom is valid, we're done.
|
|
||||||
if(bottom_valid)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else if(bank == LIGHTBANK_NIGHT)
|
|
||||||
{
|
|
||||||
// For night lighting, sunlight is not propagated
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
assert("Invalid lighting bank" == NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*infostream<<"Bottom for sunlight-propagated block ("
|
|
||||||
<<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
|
|
||||||
<<std::endl;*/
|
|
||||||
|
|
||||||
// Bottom sunlight is not valid; get the block and loop to it
|
|
||||||
|
|
||||||
pos.Y--;
|
|
||||||
try{
|
|
||||||
block = getBlockNoCreate(pos);
|
|
||||||
}
|
|
||||||
catch(InvalidPositionException &e)
|
|
||||||
{
|
|
||||||
FATAL_ERROR("Invalid position");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Enable this to disable proper lighting for speeding up map
|
|
||||||
generation for testing or whatever
|
|
||||||
*/
|
|
||||||
#if 0
|
|
||||||
//if(g_settings->get(""))
|
|
||||||
{
|
|
||||||
core::map<v3s16, MapBlock*>::Iterator i;
|
|
||||||
i = blocks_to_update.getIterator();
|
|
||||||
for(; i.atEnd() == false; i++)
|
|
||||||
{
|
|
||||||
MapBlock *block = i.getNode()->getValue();
|
|
||||||
v3s16 p = block->getPos();
|
|
||||||
block->setLightingExpired(false);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if 1
|
|
||||||
{
|
|
||||||
//TimeTaker timer("unspreadLight");
|
|
||||||
unspreadLight(bank, unlight_from, light_sources, modified_blocks);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*if(debug)
|
|
||||||
{
|
|
||||||
u32 diff = modified_blocks.size() - count_was;
|
|
||||||
count_was = modified_blocks.size();
|
|
||||||
infostream<<"unspreadLight modified "<<diff<<std::endl;
|
|
||||||
}*/
|
|
||||||
|
|
||||||
{
|
|
||||||
//TimeTaker timer("spreadLight");
|
|
||||||
spreadLight(bank, light_sources, modified_blocks);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*if(debug)
|
|
||||||
{
|
|
||||||
u32 diff = modified_blocks.size() - count_was;
|
|
||||||
count_was = modified_blocks.size();
|
|
||||||
infostream<<"spreadLight modified "<<diff<<std::endl;
|
|
||||||
}*/
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
{
|
|
||||||
//MapVoxelManipulator vmanip(this);
|
|
||||||
|
|
||||||
// Make a manual voxel manipulator and load all the blocks
|
|
||||||
// that touch the requested blocks
|
|
||||||
ManualMapVoxelManipulator vmanip(this);
|
|
||||||
|
|
||||||
{
|
|
||||||
//TimeTaker timer("initialEmerge");
|
|
||||||
|
|
||||||
core::map<v3s16, MapBlock*>::Iterator i;
|
|
||||||
i = blocks_to_update.getIterator();
|
|
||||||
for(; i.atEnd() == false; i++)
|
|
||||||
{
|
|
||||||
MapBlock *block = i.getNode()->getValue();
|
|
||||||
v3s16 p = block->getPos();
|
|
||||||
|
|
||||||
// Add all surrounding blocks
|
|
||||||
vmanip.initialEmerge(p - v3s16(1,1,1), p + v3s16(1,1,1));
|
|
||||||
|
|
||||||
/*
|
|
||||||
Add all surrounding blocks that have up-to-date lighting
|
|
||||||
NOTE: This doesn't quite do the job (not everything
|
|
||||||
appropriate is lighted)
|
|
||||||
*/
|
|
||||||
/*for(s16 z=-1; z<=1; z++)
|
|
||||||
for(s16 y=-1; y<=1; y++)
|
|
||||||
for(s16 x=-1; x<=1; x++)
|
|
||||||
{
|
|
||||||
v3s16 p2 = p + v3s16(x,y,z);
|
|
||||||
MapBlock *block = getBlockNoCreateNoEx(p2);
|
|
||||||
if(block == NULL)
|
|
||||||
continue;
|
|
||||||
if(block->isDummy())
|
|
||||||
continue;
|
|
||||||
if(block->getLightingExpired())
|
|
||||||
continue;
|
|
||||||
vmanip.initialEmerge(p2, p2);
|
|
||||||
}*/
|
|
||||||
|
|
||||||
// Lighting of block will be updated completely
|
|
||||||
block->setLightingExpired(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
//TimeTaker timer("unSpreadLight");
|
|
||||||
vmanip.unspreadLight(bank, unlight_from, light_sources, nodemgr);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
//TimeTaker timer("spreadLight");
|
|
||||||
vmanip.spreadLight(bank, light_sources, nodemgr);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
//TimeTaker timer("blitBack");
|
|
||||||
vmanip.blitBack(modified_blocks);
|
|
||||||
}
|
|
||||||
/*infostream<<"emerge_time="<<emerge_time<<std::endl;
|
|
||||||
emerge_time = 0;*/
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
//m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Map::updateLighting(std::map<v3s16, MapBlock*> & a_blocks,
|
|
||||||
std::map<v3s16, MapBlock*> & modified_blocks)
|
|
||||||
{
|
|
||||||
updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
|
|
||||||
updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
|
|
||||||
|
|
||||||
/*
|
|
||||||
Update information about whether day and night light differ
|
|
||||||
*/
|
|
||||||
for(std::map<v3s16, MapBlock*>::iterator
|
|
||||||
i = modified_blocks.begin();
|
|
||||||
i != modified_blocks.end(); ++i)
|
|
||||||
{
|
|
||||||
MapBlock *block = i->second;
|
|
||||||
block->expireDayNightDiff();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Map::addNodeAndUpdate(v3s16 p, MapNode n,
|
void Map::addNodeAndUpdate(v3s16 p, MapNode n,
|
||||||
std::map<v3s16, MapBlock*> &modified_blocks,
|
std::map<v3s16, MapBlock*> &modified_blocks,
|
||||||
bool remove_metadata)
|
bool remove_metadata)
|
||||||
|
16
src/map.h
16
src/map.h
@ -208,22 +208,6 @@ public:
|
|||||||
// position is valid, otherwise false
|
// position is valid, otherwise false
|
||||||
MapNode getNodeNoEx(v3s16 p, bool *is_valid_position = NULL);
|
MapNode getNodeNoEx(v3s16 p, bool *is_valid_position = NULL);
|
||||||
|
|
||||||
void unspreadLight(enum LightBank bank,
|
|
||||||
std::map<v3s16, u8> & from_nodes,
|
|
||||||
std::set<v3s16> & light_sources,
|
|
||||||
std::map<v3s16, MapBlock*> & modified_blocks);
|
|
||||||
|
|
||||||
void spreadLight(enum LightBank bank,
|
|
||||||
std::set<v3s16> & from_nodes,
|
|
||||||
std::map<v3s16, MapBlock*> & modified_blocks);
|
|
||||||
|
|
||||||
void updateLighting(enum LightBank bank,
|
|
||||||
std::map<v3s16, MapBlock*> & a_blocks,
|
|
||||||
std::map<v3s16, MapBlock*> & modified_blocks);
|
|
||||||
|
|
||||||
void updateLighting(std::map<v3s16, MapBlock*> & a_blocks,
|
|
||||||
std::map<v3s16, MapBlock*> & modified_blocks);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
These handle lighting but not faces.
|
These handle lighting but not faces.
|
||||||
*/
|
*/
|
||||||
|
@ -30,6 +30,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||||||
#include "util/serialize.h"
|
#include "util/serialize.h"
|
||||||
#include "serialization.h"
|
#include "serialization.h"
|
||||||
#include "filesys.h"
|
#include "filesys.h"
|
||||||
|
#include "voxelalgorithms.h"
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
@ -202,7 +203,7 @@ bool Schematic::placeOnVManip(MMVManip *vm, v3s16 p, u32 flags,
|
|||||||
return vm->m_area.contains(VoxelArea(p, p + s - v3s16(1,1,1)));
|
return vm->m_area.contains(VoxelArea(p, p + s - v3s16(1,1,1)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Schematic::placeOnMap(Map *map, v3s16 p, u32 flags,
|
void Schematic::placeOnMap(ServerMap *map, v3s16 p, u32 flags,
|
||||||
Rotation rot, bool force_place)
|
Rotation rot, bool force_place)
|
||||||
{
|
{
|
||||||
std::map<v3s16, MapBlock *> lighting_modified_blocks;
|
std::map<v3s16, MapBlock *> lighting_modified_blocks;
|
||||||
@ -238,15 +239,10 @@ void Schematic::placeOnMap(Map *map, v3s16 p, u32 flags,
|
|||||||
|
|
||||||
blitToVManip(&vm, p, rot, force_place);
|
blitToVManip(&vm, p, rot, force_place);
|
||||||
|
|
||||||
vm.blitBackAll(&modified_blocks);
|
voxalgo::blit_back_with_light(map, &vm, &modified_blocks);
|
||||||
|
|
||||||
//// Carry out post-map-modification actions
|
//// Carry out post-map-modification actions
|
||||||
|
|
||||||
//// Update lighting
|
|
||||||
// TODO: Optimize this by using Mapgen::calcLighting() instead
|
|
||||||
lighting_modified_blocks.insert(modified_blocks.begin(), modified_blocks.end());
|
|
||||||
map->updateLighting(lighting_modified_blocks, modified_blocks);
|
|
||||||
|
|
||||||
//// Create & dispatch map modification events to observers
|
//// Create & dispatch map modification events to observers
|
||||||
MapEditEvent event;
|
MapEditEvent event;
|
||||||
event.type = MEET_OTHER;
|
event.type = MEET_OTHER;
|
||||||
|
@ -25,6 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||||||
#include "util/string.h"
|
#include "util/string.h"
|
||||||
|
|
||||||
class Map;
|
class Map;
|
||||||
|
class ServerMap;
|
||||||
class Mapgen;
|
class Mapgen;
|
||||||
class MMVManip;
|
class MMVManip;
|
||||||
class PseudoRandom;
|
class PseudoRandom;
|
||||||
@ -108,7 +109,7 @@ public:
|
|||||||
|
|
||||||
void blitToVManip(MMVManip *vm, v3s16 p, Rotation rot, bool force_place);
|
void blitToVManip(MMVManip *vm, v3s16 p, Rotation rot, bool force_place);
|
||||||
bool placeOnVManip(MMVManip *vm, v3s16 p, u32 flags, Rotation rot, bool force_place);
|
bool placeOnVManip(MMVManip *vm, v3s16 p, u32 flags, Rotation rot, bool force_place);
|
||||||
void placeOnMap(Map *map, v3s16 p, u32 flags, Rotation rot, bool force_place);
|
void placeOnMap(ServerMap *map, v3s16 p, u32 flags, Rotation rot, bool force_place);
|
||||||
|
|
||||||
void applyProbabilities(v3s16 p0,
|
void applyProbabilities(v3s16 p0,
|
||||||
std::vector<std::pair<v3s16, u8> > *plist,
|
std::vector<std::pair<v3s16, u8> > *plist,
|
||||||
|
@ -1357,7 +1357,9 @@ int ModApiMapgen::l_place_schematic(lua_State *L)
|
|||||||
{
|
{
|
||||||
MAP_LOCK_REQUIRED;
|
MAP_LOCK_REQUIRED;
|
||||||
|
|
||||||
Map *map = &(getEnv(L)->getMap());
|
GET_ENV_PTR;
|
||||||
|
|
||||||
|
ServerMap *map = &(env->getServerMap());
|
||||||
SchematicManager *schemmgr = getServer(L)->getEmergeManager()->schemmgr;
|
SchematicManager *schemmgr = getServer(L)->getEmergeManager()->schemmgr;
|
||||||
|
|
||||||
//// Read position
|
//// Read position
|
||||||
|
@ -27,6 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||||||
#include "map.h"
|
#include "map.h"
|
||||||
#include "server.h"
|
#include "server.h"
|
||||||
#include "mapgen.h"
|
#include "mapgen.h"
|
||||||
|
#include "voxelalgorithms.h"
|
||||||
|
|
||||||
// garbage collector
|
// garbage collector
|
||||||
int LuaVoxelManip::gc_object(lua_State *L)
|
int LuaVoxelManip::gc_object(lua_State *L)
|
||||||
@ -109,10 +110,24 @@ int LuaVoxelManip::l_write_to_map(lua_State *L)
|
|||||||
MAP_LOCK_REQUIRED;
|
MAP_LOCK_REQUIRED;
|
||||||
|
|
||||||
LuaVoxelManip *o = checkobject(L, 1);
|
LuaVoxelManip *o = checkobject(L, 1);
|
||||||
MMVManip *vm = o->vm;
|
GET_ENV_PTR;
|
||||||
|
ServerMap *map = &(env->getServerMap());
|
||||||
|
if (o->is_mapgen_vm) {
|
||||||
|
o->vm->blitBackAll(&(o->modified_blocks));
|
||||||
|
} else {
|
||||||
|
voxalgo::blit_back_with_light(map, o->vm,
|
||||||
|
&(o->modified_blocks));
|
||||||
|
}
|
||||||
|
|
||||||
vm->blitBackAll(&o->modified_blocks);
|
MapEditEvent event;
|
||||||
|
event.type = MEET_OTHER;
|
||||||
|
for (std::map<v3s16, MapBlock *>::iterator it = o->modified_blocks.begin();
|
||||||
|
it != o->modified_blocks.end(); ++it)
|
||||||
|
event.modified_blocks.insert(it->first);
|
||||||
|
|
||||||
|
map->dispatchEvent(&event);
|
||||||
|
|
||||||
|
o->modified_blocks.clear();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -322,33 +337,6 @@ int LuaVoxelManip::l_set_param2_data(lua_State *L)
|
|||||||
|
|
||||||
int LuaVoxelManip::l_update_map(lua_State *L)
|
int LuaVoxelManip::l_update_map(lua_State *L)
|
||||||
{
|
{
|
||||||
GET_ENV_PTR;
|
|
||||||
|
|
||||||
LuaVoxelManip *o = checkobject(L, 1);
|
|
||||||
if (o->is_mapgen_vm)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
Map *map = &(env->getMap());
|
|
||||||
|
|
||||||
// TODO: Optimize this by using Mapgen::calcLighting() instead
|
|
||||||
std::map<v3s16, MapBlock *> lighting_mblocks;
|
|
||||||
std::map<v3s16, MapBlock *> *mblocks = &o->modified_blocks;
|
|
||||||
|
|
||||||
lighting_mblocks.insert(mblocks->begin(), mblocks->end());
|
|
||||||
|
|
||||||
map->updateLighting(lighting_mblocks, *mblocks);
|
|
||||||
|
|
||||||
MapEditEvent event;
|
|
||||||
event.type = MEET_OTHER;
|
|
||||||
for (std::map<v3s16, MapBlock *>::iterator
|
|
||||||
it = mblocks->begin();
|
|
||||||
it != mblocks->end(); ++it)
|
|
||||||
event.modified_blocks.insert(it->first);
|
|
||||||
|
|
||||||
map->dispatchEvent(&event);
|
|
||||||
|
|
||||||
mblocks->clear();
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,6 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||||||
#include "serverenvironment.h"
|
#include "serverenvironment.h"
|
||||||
#include "nodedef.h"
|
#include "nodedef.h"
|
||||||
#include "treegen.h"
|
#include "treegen.h"
|
||||||
|
#include "voxelalgorithms.h"
|
||||||
|
|
||||||
namespace treegen
|
namespace treegen
|
||||||
{
|
{
|
||||||
@ -125,12 +126,8 @@ treegen::error spawn_ltree(ServerEnvironment *env, v3s16 p0,
|
|||||||
if (e != SUCCESS)
|
if (e != SUCCESS)
|
||||||
return e;
|
return e;
|
||||||
|
|
||||||
vmanip.blitBackAll(&modified_blocks);
|
voxalgo::blit_back_with_light(map, &vmanip, &modified_blocks);
|
||||||
|
|
||||||
// update lighting
|
|
||||||
std::map<v3s16, MapBlock*> lighting_modified_blocks;
|
|
||||||
lighting_modified_blocks.insert(modified_blocks.begin(), modified_blocks.end());
|
|
||||||
map->updateLighting(lighting_modified_blocks, modified_blocks);
|
|
||||||
// Send a MEET_OTHER event
|
// Send a MEET_OTHER event
|
||||||
MapEditEvent event;
|
MapEditEvent event;
|
||||||
event.type = MEET_OTHER;
|
event.type = MEET_OTHER;
|
||||||
|
@ -542,6 +542,21 @@ void spread_light(Map *map, INodeDefManager *nodemgr, LightBank bank,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct SunlightPropagationUnit{
|
||||||
|
v2s16 relative_pos;
|
||||||
|
bool is_sunlit;
|
||||||
|
|
||||||
|
SunlightPropagationUnit(v2s16 relpos, bool sunlit):
|
||||||
|
relative_pos(relpos),
|
||||||
|
is_sunlit(sunlit)
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SunlightPropagationData{
|
||||||
|
std::vector<SunlightPropagationUnit> data;
|
||||||
|
v3s16 target_block;
|
||||||
|
};
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* Returns true if the node gets sunlight from the
|
* Returns true if the node gets sunlight from the
|
||||||
* node above it.
|
* node above it.
|
||||||
@ -753,7 +768,7 @@ void update_lighting_nodes(Map *map,
|
|||||||
for (u8 i = 0; i <= LIGHT_SUN; i++) {
|
for (u8 i = 0; i <= LIGHT_SUN; i++) {
|
||||||
const std::vector<ChangingLight> &lights = light_sources.lights[i];
|
const std::vector<ChangingLight> &lights = light_sources.lights[i];
|
||||||
for (std::vector<ChangingLight>::const_iterator it = lights.begin();
|
for (std::vector<ChangingLight>::const_iterator it = lights.begin();
|
||||||
it < lights.end(); it++) {
|
it < lights.end(); ++it) {
|
||||||
MapNode n = it->block->getNodeNoCheck(it->rel_position,
|
MapNode n = it->block->getNodeNoCheck(it->rel_position,
|
||||||
&is_valid_position);
|
&is_valid_position);
|
||||||
n.setLight(bank, i, ndef);
|
n.setLight(bank, i, ndef);
|
||||||
@ -817,8 +832,10 @@ void update_block_border_lighting(Map *map, MapBlock *block,
|
|||||||
bool is_valid_position;
|
bool is_valid_position;
|
||||||
for (s32 i = 0; i < 2; i++) {
|
for (s32 i = 0; i < 2; i++) {
|
||||||
LightBank bank = banks[i];
|
LightBank bank = banks[i];
|
||||||
UnlightQueue disappearing_lights(256);
|
// Since invalid light is not common, do not allocate
|
||||||
ReLightQueue light_sources(256);
|
// memory if not needed.
|
||||||
|
UnlightQueue disappearing_lights(0);
|
||||||
|
ReLightQueue light_sources(0);
|
||||||
// Get incorrect lights
|
// Get incorrect lights
|
||||||
for (direction d = 0; d < 6; d++) {
|
for (direction d = 0; d < 6; d++) {
|
||||||
// For each direction
|
// For each direction
|
||||||
@ -873,7 +890,7 @@ void update_block_border_lighting(Map *map, MapBlock *block,
|
|||||||
for (u8 i = 0; i <= LIGHT_SUN; i++) {
|
for (u8 i = 0; i <= LIGHT_SUN; i++) {
|
||||||
const std::vector<ChangingLight> &lights = light_sources.lights[i];
|
const std::vector<ChangingLight> &lights = light_sources.lights[i];
|
||||||
for (std::vector<ChangingLight>::const_iterator it = lights.begin();
|
for (std::vector<ChangingLight>::const_iterator it = lights.begin();
|
||||||
it < lights.end(); it++) {
|
it < lights.end(); ++it) {
|
||||||
MapNode n = it->block->getNodeNoCheck(it->rel_position,
|
MapNode n = it->block->getNodeNoCheck(it->rel_position,
|
||||||
&is_valid_position);
|
&is_valid_position);
|
||||||
n.setLight(bank, i, ndef);
|
n.setLight(bank, i, ndef);
|
||||||
@ -885,6 +902,352 @@ void update_block_border_lighting(Map *map, MapBlock *block,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Resets the lighting of the given VoxelManipulator to
|
||||||
|
* complete darkness and full sunlight.
|
||||||
|
* Operates in one map sector.
|
||||||
|
*
|
||||||
|
* \param offset contains the least x and z node coordinates
|
||||||
|
* of the map sector.
|
||||||
|
* \param light incoming sunlight, light[x][z] is true if there
|
||||||
|
* is sunlight above the voxel manipulator at the given x-z coordinates.
|
||||||
|
* The array's indices are relative node coordinates in the sector.
|
||||||
|
* After the procedure returns, this contains outgoing light at
|
||||||
|
* the bottom of the voxel manipulator.
|
||||||
|
*/
|
||||||
|
void fill_with_sunlight(MMVManip *vm, INodeDefManager *ndef, v2s16 offset,
|
||||||
|
bool light[MAP_BLOCKSIZE][MAP_BLOCKSIZE])
|
||||||
|
{
|
||||||
|
// Distance in array between two nodes on top of each other.
|
||||||
|
s16 ystride = vm->m_area.getExtent().X;
|
||||||
|
// Cache the ignore node.
|
||||||
|
MapNode ignore = MapNode(CONTENT_IGNORE);
|
||||||
|
// For each column of nodes:
|
||||||
|
for (s16 z = 0; z < MAP_BLOCKSIZE; z++)
|
||||||
|
for (s16 x = 0; x < MAP_BLOCKSIZE; x++) {
|
||||||
|
// Position of the column on the map.
|
||||||
|
v2s16 realpos = offset + v2s16(x, z);
|
||||||
|
// Array indices in the voxel manipulator
|
||||||
|
s32 maxindex = vm->m_area.index(realpos.X, vm->m_area.MaxEdge.Y,
|
||||||
|
realpos.Y);
|
||||||
|
s32 minindex = vm->m_area.index(realpos.X, vm->m_area.MinEdge.Y,
|
||||||
|
realpos.Y);
|
||||||
|
// True if the current node has sunlight.
|
||||||
|
bool lig = light[z][x];
|
||||||
|
// For each node, downwards:
|
||||||
|
for (s32 i = maxindex; i >= minindex; i -= ystride) {
|
||||||
|
MapNode *n;
|
||||||
|
if (vm->m_flags[i] & VOXELFLAG_NO_DATA)
|
||||||
|
n = &ignore;
|
||||||
|
else
|
||||||
|
n = &vm->m_data[i];
|
||||||
|
// Ignore IGNORE nodes, these are not generated yet.
|
||||||
|
if(n->getContent() == CONTENT_IGNORE)
|
||||||
|
continue;
|
||||||
|
const ContentFeatures &f = ndef->get(n->getContent());
|
||||||
|
if (lig && !f.sunlight_propagates)
|
||||||
|
// Sunlight is stopped.
|
||||||
|
lig = false;
|
||||||
|
// Reset light
|
||||||
|
n->setLight(LIGHTBANK_DAY, lig ? 15 : 0, f);
|
||||||
|
n->setLight(LIGHTBANK_NIGHT, 0, f);
|
||||||
|
}
|
||||||
|
// Output outgoing light.
|
||||||
|
light[z][x] = lig;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Returns incoming sunlight for one map block.
|
||||||
|
* If block above is not found, it is loaded.
|
||||||
|
*
|
||||||
|
* \param pos position of the map block that gets the sunlight.
|
||||||
|
* \param light incoming sunlight, light[z][x] is true if there
|
||||||
|
* is sunlight above the block at the given z-x relative
|
||||||
|
* node coordinates.
|
||||||
|
*/
|
||||||
|
void is_sunlight_above_block(ServerMap *map, mapblock_v3 pos,
|
||||||
|
INodeDefManager *ndef, bool light[MAP_BLOCKSIZE][MAP_BLOCKSIZE])
|
||||||
|
{
|
||||||
|
mapblock_v3 source_block_pos = pos + v3s16(0, 1, 0);
|
||||||
|
// Get or load source block.
|
||||||
|
// It might take a while to load, but correcting incorrect
|
||||||
|
// sunlight may be even slower.
|
||||||
|
MapBlock *source_block = map->emergeBlock(source_block_pos, false);
|
||||||
|
// Trust only generated blocks.
|
||||||
|
if (source_block == NULL || source_block->isDummy()
|
||||||
|
|| !source_block->isGenerated()) {
|
||||||
|
// But if there is no block above, then use heuristics
|
||||||
|
bool sunlight = true;
|
||||||
|
MapBlock *node_block = map->getBlockNoCreateNoEx(pos);
|
||||||
|
if (node_block == NULL)
|
||||||
|
// This should not happen.
|
||||||
|
sunlight = false;
|
||||||
|
else
|
||||||
|
sunlight = !node_block->getIsUnderground();
|
||||||
|
for (s16 z = 0; z < MAP_BLOCKSIZE; z++)
|
||||||
|
for (s16 x = 0; x < MAP_BLOCKSIZE; x++)
|
||||||
|
light[z][x] = sunlight;
|
||||||
|
} else {
|
||||||
|
// Dummy boolean, the position is valid.
|
||||||
|
bool is_valid_position;
|
||||||
|
// For each column:
|
||||||
|
for (s16 z = 0; z < MAP_BLOCKSIZE; z++)
|
||||||
|
for (s16 x = 0; x < MAP_BLOCKSIZE; x++) {
|
||||||
|
// Get the bottom block.
|
||||||
|
MapNode above = source_block->getNodeNoCheck(x, 0, z,
|
||||||
|
&is_valid_position);
|
||||||
|
light[z][x] = above.getLight(LIGHTBANK_DAY, ndef) == LIGHT_SUN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Propagates sunlight down in a given map block.
|
||||||
|
*
|
||||||
|
* \param data contains incoming sunlight and shadow and
|
||||||
|
* the coordinates of the target block.
|
||||||
|
* \param unlight propagated shadow is inserted here
|
||||||
|
* \param relight propagated sunlight is inserted here
|
||||||
|
*
|
||||||
|
* \returns true if the block was modified, false otherwise.
|
||||||
|
*/
|
||||||
|
bool propagate_block_sunlight(Map *map, INodeDefManager *ndef,
|
||||||
|
SunlightPropagationData *data, UnlightQueue *unlight, ReLightQueue *relight)
|
||||||
|
{
|
||||||
|
bool modified = false;
|
||||||
|
// Get the block.
|
||||||
|
MapBlock *block = map->getBlockNoCreateNoEx(data->target_block);
|
||||||
|
if (block == NULL || block->isDummy()) {
|
||||||
|
// The work is done if the block does not contain data.
|
||||||
|
data->data.clear();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Dummy boolean
|
||||||
|
bool is_valid;
|
||||||
|
// For each changing column of nodes:
|
||||||
|
size_t index;
|
||||||
|
for (index = 0; index < data->data.size(); index++) {
|
||||||
|
SunlightPropagationUnit it = data->data[index];
|
||||||
|
// Relative position of the currently inspected node.
|
||||||
|
relative_v3 current_pos(it.relative_pos.X, MAP_BLOCKSIZE - 1,
|
||||||
|
it.relative_pos.Y);
|
||||||
|
if (it.is_sunlit) {
|
||||||
|
// Propagate sunlight.
|
||||||
|
// For each node downwards:
|
||||||
|
for (; current_pos.Y >= 0; current_pos.Y--) {
|
||||||
|
MapNode n = block->getNodeNoCheck(current_pos, &is_valid);
|
||||||
|
const ContentFeatures &f = ndef->get(n);
|
||||||
|
if (n.getLightRaw(LIGHTBANK_DAY, f) < LIGHT_SUN
|
||||||
|
&& f.sunlight_propagates) {
|
||||||
|
// This node gets sunlight.
|
||||||
|
n.setLight(LIGHTBANK_DAY, LIGHT_SUN, f);
|
||||||
|
block->setNodeNoCheck(current_pos, n);
|
||||||
|
modified = true;
|
||||||
|
relight->push(LIGHT_SUN, current_pos, data->target_block,
|
||||||
|
block, 4);
|
||||||
|
} else {
|
||||||
|
// Light already valid, propagation stopped.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Propagate shadow.
|
||||||
|
// For each node downwards:
|
||||||
|
for (; current_pos.Y >= 0; current_pos.Y--) {
|
||||||
|
MapNode n = block->getNodeNoCheck(current_pos, &is_valid);
|
||||||
|
const ContentFeatures &f = ndef->get(n);
|
||||||
|
if (n.getLightRaw(LIGHTBANK_DAY, f) == LIGHT_SUN) {
|
||||||
|
// The sunlight is no longer valid.
|
||||||
|
n.setLight(LIGHTBANK_DAY, 0, f);
|
||||||
|
block->setNodeNoCheck(current_pos, n);
|
||||||
|
modified = true;
|
||||||
|
unlight->push(LIGHT_SUN, current_pos, data->target_block,
|
||||||
|
block, 4);
|
||||||
|
} else {
|
||||||
|
// Reached shadow, propagation stopped.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (current_pos.Y >= 0) {
|
||||||
|
// Propagation stopped, remove from data.
|
||||||
|
data->data[index] = data->data.back();
|
||||||
|
data->data.pop_back();
|
||||||
|
index--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return modified;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Borders of a map block in relative node coordinates.
|
||||||
|
* The areas do not overlap.
|
||||||
|
* Compatible with type 'direction'.
|
||||||
|
*/
|
||||||
|
const VoxelArea block_pad[] = {
|
||||||
|
VoxelArea(v3s16(15, 0, 0), v3s16(15, 15, 15)), //X+
|
||||||
|
VoxelArea(v3s16(1, 15, 0), v3s16(14, 15, 15)), //Y+
|
||||||
|
VoxelArea(v3s16(1, 1, 15), v3s16(14, 14, 15)), //Z+
|
||||||
|
VoxelArea(v3s16(1, 1, 0), v3s16(14, 14, 0)), //Z-
|
||||||
|
VoxelArea(v3s16(1, 0, 0), v3s16(14, 0, 15)), //Y-
|
||||||
|
VoxelArea(v3s16(0, 0, 0), v3s16(0, 15, 15)) //X-
|
||||||
|
};
|
||||||
|
|
||||||
|
void blit_back_with_light(ServerMap *map, MMVManip *vm,
|
||||||
|
std::map<v3s16, MapBlock*> *modified_blocks)
|
||||||
|
{
|
||||||
|
INodeDefManager *ndef = map->getNodeDefManager();
|
||||||
|
mapblock_v3 minblock = getNodeBlockPos(vm->m_area.MinEdge);
|
||||||
|
mapblock_v3 maxblock = getNodeBlockPos(vm->m_area.MaxEdge);
|
||||||
|
// First queue is for day light, second is for night light.
|
||||||
|
UnlightQueue unlight[] = { UnlightQueue(256), UnlightQueue(256) };
|
||||||
|
ReLightQueue relight[] = { ReLightQueue(256), ReLightQueue(256) };
|
||||||
|
// Will hold sunlight data.
|
||||||
|
bool lights[MAP_BLOCKSIZE][MAP_BLOCKSIZE];
|
||||||
|
SunlightPropagationData data;
|
||||||
|
// Dummy boolean.
|
||||||
|
bool is_valid;
|
||||||
|
|
||||||
|
// --- STEP 1: reset everything to sunlight
|
||||||
|
|
||||||
|
// For each map block:
|
||||||
|
for (s16 x = minblock.X; x <= maxblock.X; x++)
|
||||||
|
for (s16 z = minblock.Z; z <= maxblock.Z; z++) {
|
||||||
|
// Extract sunlight above.
|
||||||
|
is_sunlight_above_block(map, v3s16(x, maxblock.Y, z), ndef, lights);
|
||||||
|
v2s16 offset(x, z);
|
||||||
|
offset *= MAP_BLOCKSIZE;
|
||||||
|
// Reset the voxel manipulator.
|
||||||
|
fill_with_sunlight(vm, ndef, offset, lights);
|
||||||
|
// Copy sunlight data
|
||||||
|
data.target_block = v3s16(x, minblock.Y - 1, z);
|
||||||
|
for (s16 z = 0; z < MAP_BLOCKSIZE; z++)
|
||||||
|
for (s16 x = 0; x < MAP_BLOCKSIZE; x++)
|
||||||
|
data.data.push_back(
|
||||||
|
SunlightPropagationUnit(v2s16(x, z), lights[z][x]));
|
||||||
|
// Propagate sunlight and shadow below the voxel manipulator.
|
||||||
|
while (!data.data.empty()) {
|
||||||
|
if (propagate_block_sunlight(map, ndef, &data, &unlight[0],
|
||||||
|
&relight[0]))
|
||||||
|
(*modified_blocks)[data.target_block] =
|
||||||
|
map->getBlockNoCreateNoEx(data.target_block);
|
||||||
|
// Step downwards.
|
||||||
|
data.target_block.Y--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- STEP 2: Get nodes from borders to unlight
|
||||||
|
|
||||||
|
// In case there are unloaded holes in the voxel manipulator
|
||||||
|
// unlight each block.
|
||||||
|
// For each block:
|
||||||
|
for (s16 b_x = minblock.X; b_x <= maxblock.X; b_x++)
|
||||||
|
for (s16 b_y = minblock.Y; b_y <= maxblock.Y; b_y++)
|
||||||
|
for (s16 b_z = minblock.Z; b_z <= maxblock.Z; b_z++) {
|
||||||
|
v3s16 blockpos(b_x, b_y, b_z);
|
||||||
|
MapBlock *block = map->getBlockNoCreateNoEx(blockpos);
|
||||||
|
if (!block || block->isDummy())
|
||||||
|
// Skip not existing blocks.
|
||||||
|
continue;
|
||||||
|
v3s16 offset = block->getPosRelative();
|
||||||
|
// For each border of the block:
|
||||||
|
for (direction d = 0; d < 6; d++) {
|
||||||
|
VoxelArea a = block_pad[d];
|
||||||
|
// For each node of the border:
|
||||||
|
for (s32 x = a.MinEdge.X; x <= a.MaxEdge.X; x++)
|
||||||
|
for (s32 z = a.MinEdge.Z; z <= a.MaxEdge.Z; z++)
|
||||||
|
for (s32 y = a.MinEdge.Y; y <= a.MaxEdge.Y; y++) {
|
||||||
|
v3s16 relpos(x, y, z);
|
||||||
|
// Get old and new node
|
||||||
|
MapNode oldnode = block->getNodeNoCheck(x, y, z, &is_valid);
|
||||||
|
const ContentFeatures &oldf = ndef->get(oldnode);
|
||||||
|
MapNode newnode = vm->getNodeNoExNoEmerge(relpos + offset);
|
||||||
|
const ContentFeatures &newf = ndef->get(newnode);
|
||||||
|
// For each light bank
|
||||||
|
for (size_t b = 0; b < 2; b++) {
|
||||||
|
LightBank bank = banks[b];
|
||||||
|
u8 oldlight = oldf.param_type == CPT_LIGHT ?
|
||||||
|
oldnode.getLightNoChecks(bank, &oldf):
|
||||||
|
LIGHT_SUN; // no light information, force unlighting
|
||||||
|
u8 newlight = newf.param_type == CPT_LIGHT ?
|
||||||
|
newnode.getLightNoChecks(bank, &newf):
|
||||||
|
newf.light_source;
|
||||||
|
// If the new node is dimmer, unlight.
|
||||||
|
if (oldlight > newlight) {
|
||||||
|
unlight[b].push(
|
||||||
|
oldlight, relpos, blockpos, block, 6);
|
||||||
|
}
|
||||||
|
} // end of banks
|
||||||
|
} // end of nodes
|
||||||
|
} // end of borders
|
||||||
|
} // end of blocks
|
||||||
|
|
||||||
|
// --- STEP 3: All information extracted, overwrite
|
||||||
|
|
||||||
|
vm->blitBackAll(modified_blocks, true);
|
||||||
|
|
||||||
|
// --- STEP 4: Do unlighting
|
||||||
|
|
||||||
|
for (size_t bank = 0; bank < 2; bank++) {
|
||||||
|
LightBank b = banks[bank];
|
||||||
|
unspread_light(map, ndef, b, unlight[bank], relight[bank],
|
||||||
|
*modified_blocks);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- STEP 5: Get all newly inserted light sources
|
||||||
|
|
||||||
|
// For each block:
|
||||||
|
for (s16 b_x = minblock.X; b_x <= maxblock.X; b_x++)
|
||||||
|
for (s16 b_y = minblock.Y; b_y <= maxblock.Y; b_y++)
|
||||||
|
for (s16 b_z = minblock.Z; b_z <= maxblock.Z; b_z++) {
|
||||||
|
v3s16 blockpos(b_x, b_y, b_z);
|
||||||
|
MapBlock *block = map->getBlockNoCreateNoEx(blockpos);
|
||||||
|
if (!block || block->isDummy())
|
||||||
|
// Skip not existing blocks
|
||||||
|
continue;
|
||||||
|
// For each node in the block:
|
||||||
|
for (s32 x = 0; x < MAP_BLOCKSIZE; x++)
|
||||||
|
for (s32 z = 0; z < MAP_BLOCKSIZE; z++)
|
||||||
|
for (s32 y = 0; y < MAP_BLOCKSIZE; y++) {
|
||||||
|
v3s16 relpos(x, y, z);
|
||||||
|
MapNode node = block->getNodeNoCheck(x, y, z, &is_valid);
|
||||||
|
const ContentFeatures &f = ndef->get(node);
|
||||||
|
// For each light bank
|
||||||
|
for (size_t b = 0; b < 2; b++) {
|
||||||
|
LightBank bank = banks[b];
|
||||||
|
u8 light = f.param_type == CPT_LIGHT ?
|
||||||
|
node.getLightNoChecks(bank, &f):
|
||||||
|
f.light_source;
|
||||||
|
if (light > 1)
|
||||||
|
relight[b].push(light, relpos, blockpos, block, 6);
|
||||||
|
} // end of banks
|
||||||
|
} // end of nodes
|
||||||
|
} // end of blocks
|
||||||
|
|
||||||
|
// --- STEP 6: do light spreading
|
||||||
|
|
||||||
|
// For each light bank:
|
||||||
|
for (size_t b = 0; b < 2; b++) {
|
||||||
|
LightBank bank = banks[b];
|
||||||
|
// Sunlight is already initialized.
|
||||||
|
u8 maxlight = (b == 0) ? LIGHT_MAX : LIGHT_SUN;
|
||||||
|
// Initialize light values for light spreading.
|
||||||
|
for (u8 i = 0; i <= maxlight; i++) {
|
||||||
|
const std::vector<ChangingLight> &lights = relight[b].lights[i];
|
||||||
|
for (std::vector<ChangingLight>::const_iterator it = lights.begin();
|
||||||
|
it < lights.end(); ++it) {
|
||||||
|
MapNode n = it->block->getNodeNoCheck(it->rel_position,
|
||||||
|
&is_valid);
|
||||||
|
n.setLight(bank, i, ndef);
|
||||||
|
it->block->setNodeNoCheck(it->rel_position, n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Spread lights.
|
||||||
|
spread_light(map, ndef, bank, relight[b], *modified_blocks);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
VoxelLineIterator::VoxelLineIterator(
|
VoxelLineIterator::VoxelLineIterator(
|
||||||
const v3f &start_position,
|
const v3f &start_position,
|
||||||
const v3f &line_vector) :
|
const v3f &line_vector) :
|
||||||
|
@ -26,7 +26,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||||||
#include "util/cpp11_container.h"
|
#include "util/cpp11_container.h"
|
||||||
|
|
||||||
class Map;
|
class Map;
|
||||||
|
class ServerMap;
|
||||||
class MapBlock;
|
class MapBlock;
|
||||||
|
class MMVManip;
|
||||||
|
|
||||||
namespace voxalgo
|
namespace voxalgo
|
||||||
{
|
{
|
||||||
@ -84,6 +86,17 @@ void update_lighting_nodes(
|
|||||||
void update_block_border_lighting(Map *map, MapBlock *block,
|
void update_block_border_lighting(Map *map, MapBlock *block,
|
||||||
std::map<v3s16, MapBlock*> &modified_blocks);
|
std::map<v3s16, MapBlock*> &modified_blocks);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Copies back nodes from a voxel manipulator
|
||||||
|
* to the map and updates lighting.
|
||||||
|
* For server use only.
|
||||||
|
*
|
||||||
|
* \param modified_blocks output, contains all map blocks that
|
||||||
|
* the function modified
|
||||||
|
*/
|
||||||
|
void blit_back_with_light(ServerMap *map, MMVManip *vm,
|
||||||
|
std::map<v3s16, MapBlock*> *modified_blocks);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* This class iterates trough voxels that intersect with
|
* This class iterates trough voxels that intersect with
|
||||||
* a line. The collision detection does not see nodeboxes,
|
* a line. The collision detection does not see nodeboxes,
|
||||||
|
Loading…
Reference in New Issue
Block a user