mirror of
https://github.com/luanti-org/luanti.git
synced 2025-06-27 16:36:03 +00:00
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
10 changed files with 408 additions and 636 deletions
565
src/map.cpp
565
src/map.cpp
|
@ -235,571 +235,6 @@ void Map::setNode(v3s16 p, MapNode & 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,
|
||||
std::map<v3s16, MapBlock*> &modified_blocks,
|
||||
bool remove_metadata)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue