Lighting: Update lighting at block loading

This commit updates mapblocks' light if necessary when they are loaded.
This removes ghost lighting.
This commit is contained in:
Dániel Juhász 2016-10-27 23:25:44 +02:00 committed by paramat
parent 2bd10022cb
commit f17c9c45dc
7 changed files with 274 additions and 122 deletions

@ -283,12 +283,6 @@ void RemoteClient::GetNextBlocks (
surely_not_found_on_disk = true;
}
// Block is valid if lighting is up-to-date and data exists
if(block->isValid() == false)
{
block_is_invalid = true;
}
if(block->isGenerated() == false)
block_is_invalid = true;

@ -824,7 +824,7 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n,
// Update lighting
std::vector<std::pair<v3s16, MapNode> > oldnodes;
oldnodes.push_back(std::pair<v3s16, MapNode>(p, oldnode));
voxalgo::update_lighting_nodes(this, m_nodedef, oldnodes, modified_blocks);
voxalgo::update_lighting_nodes(this, oldnodes, modified_blocks);
for(std::map<v3s16, MapBlock*>::iterator
i = modified_blocks.begin();
@ -1523,7 +1523,7 @@ void Map::transformLiquids(std::map<v3s16, MapBlock*> &modified_blocks)
for (std::deque<v3s16>::iterator iter = must_reflow.begin(); iter != must_reflow.end(); ++iter)
m_transforming_liquid.push_back(*iter);
voxalgo::update_lighting_nodes(this, m_nodedef, changed_nodes, modified_blocks);
voxalgo::update_lighting_nodes(this, changed_nodes, modified_blocks);
/* ----------------------------------------------------------------------
@ -1955,27 +1955,10 @@ void ServerMap::finishBlockMake(BlockMakeData *data,
v3s16 bpmax = data->blockpos_max;
v3s16 extra_borders(1, 1, 1);
v3s16 full_bpmin = bpmin - extra_borders;
v3s16 full_bpmax = bpmax + extra_borders;
bool enable_mapgen_debug_info = m_emerge->enable_mapgen_debug_info;
EMERGE_DBG_OUT("finishBlockMake(): " PP(bpmin) " - " PP(bpmax));
/*
Set lighting to non-expired state in all of them.
This is cheating, but it is not fast enough if all of them
would actually be updated.
*/
for (s16 x = full_bpmin.X; x <= full_bpmax.X; x++)
for (s16 z = full_bpmin.Z; z <= full_bpmax.Z; z++)
for (s16 y = full_bpmin.Y; y <= full_bpmax.Y; y++) {
MapBlock *block = emergeBlock(v3s16(x, y, z), false);
if (!block)
continue;
block->setLightingExpired(false);
}
/*
Blit generated stuff to map
NOTE: blitBackAll adds nearly everything to changed_blocks
@ -2991,7 +2974,6 @@ void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool
// We just loaded it from, so it's up-to-date.
block->resetModified();
}
catch(SerializationError &e)
{
@ -3015,71 +2997,80 @@ MapBlock* ServerMap::loadBlock(v3s16 blockpos)
{
DSTACK(FUNCTION_NAME);
bool created_new = (getBlockNoCreateNoEx(blockpos) == NULL);
v2s16 p2d(blockpos.X, blockpos.Z);
std::string ret;
dbase->loadBlock(blockpos, &ret);
if (ret != "") {
loadBlock(&ret, blockpos, createSector(p2d), false);
return getBlockNoCreateNoEx(blockpos);
}
// Not found in database, try the files
} else {
// Not found in database, try the files
// The directory layout we're going to load from.
// 1 - original sectors/xxxxzzzz/
// 2 - new sectors2/xxx/zzz/
// If we load from anything but the latest structure, we will
// immediately save to the new one, and remove the old.
int loadlayout = 1;
std::string sectordir1 = getSectorDir(p2d, 1);
std::string sectordir;
if(fs::PathExists(sectordir1))
{
sectordir = sectordir1;
}
else
{
loadlayout = 2;
sectordir = getSectorDir(p2d, 2);
}
// The directory layout we're going to load from.
// 1 - original sectors/xxxxzzzz/
// 2 - new sectors2/xxx/zzz/
// If we load from anything but the latest structure, we will
// immediately save to the new one, and remove the old.
int loadlayout = 1;
std::string sectordir1 = getSectorDir(p2d, 1);
std::string sectordir;
if (fs::PathExists(sectordir1)) {
sectordir = sectordir1;
} else {
loadlayout = 2;
sectordir = getSectorDir(p2d, 2);
}
/*
/*
Make sure sector is loaded
*/
*/
MapSector *sector = getSectorNoGenerateNoEx(p2d);
if(sector == NULL)
{
try{
sector = loadSectorMeta(sectordir, loadlayout != 2);
MapSector *sector = getSectorNoGenerateNoEx(p2d);
if (sector == NULL) {
try {
sector = loadSectorMeta(sectordir, loadlayout != 2);
} catch(InvalidFilenameException &e) {
return NULL;
} catch(FileNotGoodException &e) {
return NULL;
} catch(std::exception &e) {
return NULL;
}
}
catch(InvalidFilenameException &e)
{
return NULL;
}
catch(FileNotGoodException &e)
{
return NULL;
}
catch(std::exception &e)
{
/*
Make sure file exists
*/
std::string blockfilename = getBlockFilename(blockpos);
if (fs::PathExists(sectordir + DIR_DELIM + blockfilename) == false)
return NULL;
/*
Load block and save it to the database
*/
loadBlock(sectordir, blockfilename, sector, true);
}
MapBlock *block = getBlockNoCreateNoEx(blockpos);
if (created_new && (block != NULL)) {
std::map<v3s16, MapBlock*> modified_blocks;
// Fix lighting if necessary
voxalgo::update_block_border_lighting(this, block, modified_blocks);
if (!modified_blocks.empty()) {
//Modified lighting, send event
MapEditEvent event;
event.type = MEET_OTHER;
std::map<v3s16, MapBlock *>::iterator it;
for (it = modified_blocks.begin();
it != modified_blocks.end(); ++it)
event.modified_blocks.insert(it->first);
dispatchEvent(&event);
}
}
/*
Make sure file exists
*/
std::string blockfilename = getBlockFilename(blockpos);
if(fs::PathExists(sectordir + DIR_DELIM + blockfilename) == false)
return NULL;
/*
Load block and save it to the database
*/
loadBlock(sectordir, blockfilename, sector, true);
return getBlockNoCreateNoEx(blockpos);
return block;
}
bool ServerMap::deleteBlock(v3s16 blockpos)

@ -73,7 +73,7 @@ MapBlock::MapBlock(Map *parent, v3s16 pos, IGameDef *gamedef, bool dummy):
m_modified(MOD_STATE_WRITE_NEEDED),
m_modified_reason(MOD_REASON_INITIAL),
is_underground(false),
m_lighting_expired(true),
m_lighting_complete(0xFFFF),
m_day_night_differs(false),
m_day_night_differs_expired(true),
m_generated(false),
@ -571,11 +571,12 @@ void MapBlock::serialize(std::ostream &os, u8 version, bool disk)
flags |= 0x01;
if(getDayNightDiff())
flags |= 0x02;
if(m_lighting_expired)
flags |= 0x04;
if(m_generated == false)
flags |= 0x08;
writeU8(os, flags);
if (version >= 27) {
writeU16(os, m_lighting_complete);
}
/*
Bulk node data
@ -672,7 +673,11 @@ void MapBlock::deSerialize(std::istream &is, u8 version, bool disk)
u8 flags = readU8(is);
is_underground = (flags & 0x01) ? true : false;
m_day_night_differs = (flags & 0x02) ? true : false;
m_lighting_expired = (flags & 0x04) ? true : false;
if (version < 27) {
m_lighting_complete = 0xFFFF;
} else {
m_lighting_complete = readU16(is);
}
m_generated = (flags & 0x08) ? false : true;
/*
@ -783,7 +788,7 @@ void MapBlock::deSerialize_pre22(std::istream &is, u8 version, bool disk)
// Initialize default flags
is_underground = false;
m_day_night_differs = false;
m_lighting_expired = false;
m_lighting_complete = 0xFFFF;
m_generated = true;
// Make a temporary buffer
@ -849,7 +854,6 @@ void MapBlock::deSerialize_pre22(std::istream &is, u8 version, bool disk)
is.read((char*)&flags, 1);
is_underground = (flags & 0x01) ? true : false;
m_day_night_differs = (flags & 0x02) ? true : false;
m_lighting_expired = (flags & 0x04) ? true : false;
if(version >= 18)
m_generated = (flags & 0x08) ? false : true;
@ -1027,10 +1031,7 @@ std::string analyze_block(MapBlock *block)
else
desc<<"is_ug [ ], ";
if(block->getLightingExpired())
desc<<"lighting_exp [X], ";
else
desc<<"lighting_exp [ ], ";
desc<<"lighting_complete: "<<block->getLightingComplete()<<", ";
if(block->isDummy())
{

@ -105,7 +105,7 @@ public:
#define MOD_REASON_INITIAL (1 << 0)
#define MOD_REASON_REALLOCATE (1 << 1)
#define MOD_REASON_SET_IS_UNDERGROUND (1 << 2)
#define MOD_REASON_SET_LIGHTING_EXPIRED (1 << 3)
#define MOD_REASON_SET_LIGHTING_COMPLETE (1 << 3)
#define MOD_REASON_SET_GENERATED (1 << 4)
#define MOD_REASON_SET_NODE (1 << 5)
#define MOD_REASON_SET_NODE_NO_CHECK (1 << 6)
@ -213,17 +213,42 @@ public:
raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_SET_IS_UNDERGROUND);
}
inline void setLightingExpired(bool expired)
inline void setLightingComplete(u16 newflags)
{
if (expired != m_lighting_expired){
m_lighting_expired = expired;
raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_SET_LIGHTING_EXPIRED);
if (newflags != m_lighting_complete) {
m_lighting_complete = newflags;
raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_SET_LIGHTING_COMPLETE);
}
}
inline bool getLightingExpired()
inline u16 getLightingComplete()
{
return m_lighting_expired;
return m_lighting_complete;
}
inline void setLightingComplete(LightBank bank, u8 direction,
bool is_complete)
{
assert(direction >= 0 && direction <= 5);
if (bank == LIGHTBANK_NIGHT) {
direction += 6;
}
u16 newflags = m_lighting_complete;
if (is_complete) {
newflags |= 1 << direction;
} else {
newflags &= ~(1 << direction);
}
setLightingComplete(newflags);
}
inline bool isLightingComplete(LightBank bank, u8 direction)
{
assert(direction >= 0 && direction <= 5);
if (bank == LIGHTBANK_NIGHT) {
direction += 6;
}
return (m_lighting_complete & (1 << direction)) != 0;
}
inline bool isGenerated()
@ -239,15 +264,6 @@ public:
}
}
inline bool isValid()
{
if (m_lighting_expired)
return false;
if (data == NULL)
return false;
return true;
}
////
//// Position stuff
////
@ -613,14 +629,14 @@ private:
*/
bool is_underground;
/*
Set to true if changes has been made that make the old lighting
values wrong but the lighting hasn't been actually updated.
If this is false, lighting is exactly right.
If this is true, lighting might be wrong or right.
/*!
* Each bit indicates if light spreading was finished
* in a direction. (Because the neighbor could also be unloaded.)
* Bits: day X+, day Y+, day Z+, day Z-, day Y-, day X-,
* night X+, night Y+, night Z+, night Z-, night Y-, night X-,
* nothing, nothing, nothing, nothing.
*/
bool m_lighting_expired;
u16 m_lighting_complete;
// Whether day and night lighting differs
bool m_day_night_differs;

@ -62,13 +62,14 @@ with this program; if not, write to the Free Software Foundation, Inc.,
24: 16-bit node ids and node timers (never released as stable)
25: Improved node timer format
26: Never written; read the same as 25
27: Added light spreading flags to blocks
*/
// This represents an uninitialized or invalid format
#define SER_FMT_VER_INVALID 255
// Highest supported serialization version
#define SER_FMT_VER_HIGHEST_READ 26
#define SER_FMT_VER_HIGHEST_READ 27
// Saved on disk version
#define SER_FMT_VER_HIGHEST_WRITE 25
#define SER_FMT_VER_HIGHEST_WRITE 27
// Lowest supported serialization version
#define SER_FMT_VER_LOWEST_READ 0
// Lowest serialization version for writing

@ -423,6 +423,7 @@ void unspread_light(Map *map, INodeDefManager *nodemgr, LightBank bank,
if (step_rel_block_pos(i, neighbor_rel_pos, neighbor_block_pos)) {
neighbor_block = map->getBlockNoCreateNoEx(neighbor_block_pos);
if (neighbor_block == NULL) {
current.block->setLightingComplete(bank, i, false);
continue;
}
} else {
@ -486,7 +487,8 @@ void unspread_light(Map *map, INodeDefManager *nodemgr, LightBank bank,
* \param modified_blocks output, all modified map blocks are added to this
*/
void spread_light(Map *map, INodeDefManager *nodemgr, LightBank bank,
LightQueue &light_sources, std::map<v3s16, MapBlock*> &modified_blocks)
LightQueue &light_sources,
std::map<v3s16, MapBlock*> &modified_blocks)
{
// The light the current node can provide to its neighbors.
u8 spreading_light;
@ -511,6 +513,7 @@ void spread_light(Map *map, INodeDefManager *nodemgr, LightBank bank,
if (step_rel_block_pos(i, neighbor_rel_pos, neighbor_block_pos)) {
neighbor_block = map->getBlockNoCreateNoEx(neighbor_block_pos);
if (neighbor_block == NULL) {
current.block->setLightingComplete(bank, i, false);
continue;
}
} else {
@ -584,10 +587,11 @@ bool is_sunlight_above(Map *map, v3s16 pos, INodeDefManager *ndef)
static const LightBank banks[] = { LIGHTBANK_DAY, LIGHTBANK_NIGHT };
void update_lighting_nodes(Map *map, INodeDefManager *ndef,
void update_lighting_nodes(Map *map,
std::vector<std::pair<v3s16, MapNode> > &oldnodes,
std::map<v3s16, MapBlock*> &modified_blocks)
{
INodeDefManager *ndef = map->getNodeDefManager();
// For node getter functions
bool is_valid_position;
@ -596,6 +600,22 @@ void update_lighting_nodes(Map *map, INodeDefManager *ndef,
LightBank bank = banks[i];
UnlightQueue disappearing_lights(256);
ReLightQueue light_sources(256);
// Nodes that are brighter than the brightest modified node was
// won't change, since they didn't get their light from a
// modified node.
u8 min_safe_light = 0;
for (std::vector<std::pair<v3s16, MapNode> >::iterator it =
oldnodes.begin(); it < oldnodes.end(); ++it) {
u8 old_light = it->second.getLight(bank, ndef);
if (old_light > min_safe_light) {
min_safe_light = old_light;
}
}
// If only one node changed, even nodes with the same brightness
// didn't get their light from the changed node.
if (oldnodes.size() > 1) {
min_safe_light++;
}
// For each changed node process sunlight and initialize
for (std::vector<std::pair<v3s16, MapNode> >::iterator it =
oldnodes.begin(); it < oldnodes.end(); ++it) {
@ -634,11 +654,9 @@ void update_lighting_nodes(Map *map, INodeDefManager *ndef,
MapNode n2 = map->getNodeNoEx(p2, &is_valid);
if (is_valid) {
u8 spread = n2.getLight(bank, ndef);
// If the neighbor is at least as bright as
// this node then its light is not from
// this node.
// Its light can spread to this node.
if (spread > new_light && spread >= old_light) {
// If it is sure that the neighbor won't be
// unlighted, its light can spread to this node.
if (spread > new_light && spread >= min_safe_light) {
new_light = spread - 1;
}
}
@ -747,6 +765,126 @@ void update_lighting_nodes(Map *map, INodeDefManager *ndef,
}
}
/*!
* Borders of a map block in relative node coordinates.
* Compatible with type 'direction'.
*/
const VoxelArea block_borders[] = {
VoxelArea(v3s16(15, 0, 0), v3s16(15, 15, 15)), //X+
VoxelArea(v3s16(0, 15, 0), v3s16(15, 15, 15)), //Y+
VoxelArea(v3s16(0, 0, 15), v3s16(15, 15, 15)), //Z+
VoxelArea(v3s16(0, 0, 0), v3s16(15, 15, 0)), //Z-
VoxelArea(v3s16(0, 0, 0), v3s16(15, 0, 15)), //Y-
VoxelArea(v3s16(0, 0, 0), v3s16(0, 15, 15)) //X-
};
/*!
* Returns true if:
* -the node has unloaded neighbors
* -the node doesn't have light
* -the node's light is the same as the maximum of
* its light source and its brightest neighbor minus one.
* .
*/
bool is_light_locally_correct(Map *map, INodeDefManager *ndef, LightBank bank,
v3s16 pos)
{
bool is_valid_position;
MapNode n = map->getNodeNoEx(pos, &is_valid_position);
const ContentFeatures &f = ndef->get(n);
if (f.param_type != CPT_LIGHT) {
return true;
}
u8 light = n.getLightNoChecks(bank, &f);
assert(f.light_source <= LIGHT_MAX);
u8 brightest_neighbor = f.light_source + 1;
for (direction d = 0; d < 6; ++d) {
MapNode n2 = map->getNodeNoEx(pos + neighbor_dirs[d],
&is_valid_position);
u8 light2 = n2.getLight(bank, ndef);
if (brightest_neighbor < light2) {
brightest_neighbor = light2;
}
}
assert(light <= LIGHT_SUN);
return brightest_neighbor == light + 1;
}
void update_block_border_lighting(Map *map, MapBlock *block,
std::map<v3s16, MapBlock*> &modified_blocks)
{
INodeDefManager *ndef = map->getNodeDefManager();
bool is_valid_position;
for (s32 i = 0; i < 2; i++) {
LightBank bank = banks[i];
UnlightQueue disappearing_lights(256);
ReLightQueue light_sources(256);
// Get incorrect lights
for (direction d = 0; d < 6; d++) {
// For each direction
// Get neighbor block
v3s16 otherpos = block->getPos() + neighbor_dirs[d];
MapBlock *other = map->getBlockNoCreateNoEx(otherpos);
if (other == NULL) {
continue;
}
// Only update if lighting was not completed.
if (block->isLightingComplete(bank, d) &&
other->isLightingComplete(bank, 5 - d))
continue;
// Reset flags
block->setLightingComplete(bank, d, true);
other->setLightingComplete(bank, 5 - d, true);
// The two blocks and their connecting surfaces
MapBlock *blocks[] = {block, other};
VoxelArea areas[] = {block_borders[d], block_borders[5 - d]};
// For both blocks
for (u8 blocknum = 0; blocknum < 2; blocknum++) {
MapBlock *b = blocks[blocknum];
VoxelArea a = areas[blocknum];
// For all nodes
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++) {
MapNode n = b->getNodeNoCheck(x, y, z,
&is_valid_position);
u8 light = n.getLight(bank, ndef);
// Sunlight is fixed
if (light < LIGHT_SUN) {
// Unlight if not correct
if (!is_light_locally_correct(map, ndef, bank,
v3s16(x, y, z) + b->getPosRelative())) {
// Initialize for unlighting
n.setLight(bank, 0, ndef);
b->setNodeNoCheck(x, y, z, n);
modified_blocks[b->getPos()]=b;
disappearing_lights.push(light,
relative_v3(x, y, z), b->getPos(), b,
6);
}
}
}
}
}
// Remove lights
unspread_light(map, ndef, bank, disappearing_lights, light_sources,
modified_blocks);
// Initialize light values for light spreading.
for (u8 i = 0; i <= LIGHT_SUN; i++) {
const std::vector<ChangingLight> &lights = light_sources.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_position);
n.setLight(bank, i, ndef);
it->block->setNodeNoCheck(it->rel_position, n);
}
}
// Spread lights.
spread_light(map, ndef, bank, light_sources, modified_blocks);
}
}
VoxelLineIterator::VoxelLineIterator(
const v3f &start_position,
const v3f &line_vector) :

@ -22,8 +22,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "voxel.h"
#include "mapnode.h"
#include <set>
#include <map>
#include "util/container.h"
#include "util/cpp11_container.h"
class Map;
class MapBlock;
@ -69,10 +69,21 @@ SunlightPropagateResult propagateSunlight(VoxelManipulator &v, VoxelArea a,
*/
void update_lighting_nodes(
Map *map,
INodeDefManager *ndef,
std::vector<std::pair<v3s16, MapNode> > &oldnodes,
std::map<v3s16, MapBlock*> &modified_blocks);
/*!
* Updates borders of the given mapblock.
* Only updates if the block was marked with incomplete
* lighting and the neighbor is also loaded.
*
* \param block the block to update
* \param modified_blocks output, contains all map blocks that
* the function modified
*/
void update_block_border_lighting(Map *map, MapBlock *block,
std::map<v3s16, MapBlock*> &modified_blocks);
/*!
* This class iterates trough voxels that intersect with
* a line. The collision detection does not see nodeboxes,