From 57e5aa662851485902575c3c747437e365bf72c8 Mon Sep 17 00:00:00 2001 From: Dániel Juhász Date: Sat, 11 Mar 2017 17:07:04 +0100 Subject: Light update for map blocks This is not really different from the light update of a voxel manipulator. This update does not assume that the lighting was correct before, therefore it is useful for correction. Also expose this function to the Lua API for light correction, and allow voxel manipulators not to update the light. --- src/map.cpp | 10 ++++ src/map.h | 10 ++++ src/script/lua_api/l_env.cpp | 31 ++++++++++ src/script/lua_api/l_env.h | 3 + src/script/lua_api/l_vmanip.cpp | 3 +- src/voxelalgorithms.cpp | 122 +++++++++++++++++++++++++++++++++++++++- src/voxelalgorithms.h | 9 +++ 7 files changed, 186 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/map.cpp b/src/map.cpp index f8bbee180..8754813dd 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -2591,6 +2591,16 @@ void ServerMap::PrintInfo(std::ostream &out) out<<"ServerMap: "; } +bool ServerMap::repairBlockLight(v3s16 blockpos, + std::map *modified_blocks) +{ + MapBlock *block = emergeBlock(blockpos, false); + if (!block || !block->isGenerated()) + return false; + voxalgo::repair_block_light(this, block, modified_blocks); + return true; +} + MMVManip::MMVManip(Map *map): VoxelManipulator(), m_is_dirty(false), diff --git a/src/map.h b/src/map.h index 744a4d1e2..739cdb59b 100644 --- a/src/map.h +++ b/src/map.h @@ -477,6 +477,16 @@ public: u64 getSeed(); s16 getWaterLevel(); + /*! + * Fixes lighting in one map block. + * May modify other blocks as well, as light can spread + * out of the specified block. + * Returns false if the block is not generated (so nothing + * changed), true otherwise. + */ + bool repairBlockLight(v3s16 blockpos, + std::map *modified_blocks); + MapSettingsManager settings_mgr; private: diff --git a/src/script/lua_api/l_env.cpp b/src/script/lua_api/l_env.cpp index 4fad7b37c..1fa7845b5 100644 --- a/src/script/lua_api/l_env.cpp +++ b/src/script/lua_api/l_env.cpp @@ -847,6 +847,36 @@ int ModApiEnvMod::l_line_of_sight(lua_State *L) return 1; } +// fix_light(p1, p2) +int ModApiEnvMod::l_fix_light(lua_State *L) +{ + GET_ENV_PTR; + + v3s16 blockpos1 = getContainerPos(read_v3s16(L, 1), MAP_BLOCKSIZE); + v3s16 blockpos2 = getContainerPos(read_v3s16(L, 2), MAP_BLOCKSIZE); + ServerMap &map = env->getServerMap(); + std::map modified_blocks; + bool success = true; + v3s16 blockpos; + for (blockpos.X = blockpos1.X; blockpos.X <= blockpos2.X; blockpos.X++) + for (blockpos.Y = blockpos1.Y; blockpos.Y <= blockpos2.Y; blockpos.Y++) + for (blockpos.Z = blockpos1.Z; blockpos.Z <= blockpos2.Z; blockpos.Z++) { + success = success & map.repairBlockLight(blockpos, &modified_blocks); + } + if (modified_blocks.size() > 0) { + MapEditEvent event; + event.type = MEET_OTHER; + for (std::map::iterator it = modified_blocks.begin(); + it != modified_blocks.end(); ++it) + event.modified_blocks.insert(it->first); + + map.dispatchEvent(&event); + } + lua_pushboolean(L, success); + + return 1; +} + // emerge_area(p1, p2, [callback, context]) // emerge mapblocks in area p1..p2, calls callback with context upon completion int ModApiEnvMod::l_emerge_area(lua_State *L) @@ -1089,6 +1119,7 @@ void ModApiEnvMod::Initialize(lua_State *L, int top) API_FCT(find_node_near); API_FCT(find_nodes_in_area); API_FCT(find_nodes_in_area_under_air); + API_FCT(fix_light); API_FCT(emerge_area); API_FCT(delete_area); API_FCT(get_perlin); diff --git a/src/script/lua_api/l_env.h b/src/script/lua_api/l_env.h index 38b2282d7..3f688b398 100644 --- a/src/script/lua_api/l_env.h +++ b/src/script/lua_api/l_env.h @@ -128,6 +128,9 @@ private: // nodenames: eg. {"ignore", "group:tree"} or "default:dirt" static int l_find_nodes_in_area_under_air(lua_State *L); + // fix_light(p1, p2) -> true/false + static int l_fix_light(lua_State *L); + // emerge_area(p1, p2) static int l_emerge_area(lua_State *L); diff --git a/src/script/lua_api/l_vmanip.cpp b/src/script/lua_api/l_vmanip.cpp index 7316fb200..254a7e5a6 100644 --- a/src/script/lua_api/l_vmanip.cpp +++ b/src/script/lua_api/l_vmanip.cpp @@ -110,9 +110,10 @@ int LuaVoxelManip::l_write_to_map(lua_State *L) MAP_LOCK_REQUIRED; LuaVoxelManip *o = checkobject(L, 1); + bool update_light = lua_isboolean(L, 2) ? lua_toboolean(L, 2) : true; GET_ENV_PTR; ServerMap *map = &(env->getServerMap()); - if (o->is_mapgen_vm) { + if (o->is_mapgen_vm || !update_light) { o->vm->blitBackAll(&(o->modified_blocks)); } else { voxalgo::blit_back_with_light(map, o->vm, diff --git a/src/voxelalgorithms.cpp b/src/voxelalgorithms.cpp index f2142717f..40f8595a7 100644 --- a/src/voxelalgorithms.cpp +++ b/src/voxelalgorithms.cpp @@ -1136,7 +1136,7 @@ void finish_bulk_light_update(Map *map, mapblock_v3 minblock, 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); + const v3s16 blockpos(b_x, b_y, b_z); MapBlock *block = map->getBlockNoCreateNoEx(blockpos); if (!block || block->isDummy()) // Skip not existing blocks @@ -1282,6 +1282,126 @@ void blit_back_with_light(ServerMap *map, MMVManip *vm, modified_blocks); } +/*! + * Resets the lighting of the given map block to + * complete darkness and full sunlight. + * + * \param light incoming sunlight, light[x][z] is true if there + * is sunlight above the map block at the given x-z coordinates. + * The array's indices are relative node coordinates in the block. + * After the procedure returns, this contains outgoing light at + * the bottom of the map block. + */ +void fill_with_sunlight(MapBlock *block, INodeDefManager *ndef, + bool light[MAP_BLOCKSIZE][MAP_BLOCKSIZE]) +{ + if (block->isDummy()) + return; + // dummy boolean + bool is_valid; + // For each column of nodes: + for (s16 z = 0; z < MAP_BLOCKSIZE; z++) + for (s16 x = 0; x < MAP_BLOCKSIZE; x++) { + // True if the current node has sunlight. + bool lig = light[z][x]; + // For each node, downwards: + for (s16 y = MAP_BLOCKSIZE - 1; y >= 0; y--) { + MapNode n = block->getNodeNoCheck(x, y, z, &is_valid); + // 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); + block->setNodeNoCheck(x, y, z, n); + } + // Output outgoing light. + light[z][x] = lig; + } +} + +void repair_block_light(ServerMap *map, MapBlock *block, + std::map *modified_blocks) +{ + if (!block || block->isDummy()) + return; + INodeDefManager *ndef = map->getNodeDefManager(); + // 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 + + mapblock_v3 blockpos = block->getPos(); + (*modified_blocks)[blockpos] = block; + // For each map block: + // Extract sunlight above. + is_sunlight_above_block(map, blockpos, ndef, lights); + // Reset the voxel manipulator. + fill_with_sunlight(block, ndef, lights); + // Copy sunlight data + data.target_block = v3s16(blockpos.X, blockpos.Y - 1, blockpos.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 + + // 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 node + 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 the new node is dimmer than sunlight, unlight. + // (if it has maximal light, it is pointless to remove + // surrounding light, as it can only become brighter) + if (LIGHT_SUN > light) { + unlight[b].push( + LIGHT_SUN, relpos, blockpos, block, 6); + } + } // end of banks + } // end of nodes + } // end of borders + + // STEP 3: Remove and spread light + + finish_bulk_light_update(map, blockpos, blockpos, unlight, relight, + modified_blocks); +} + VoxelLineIterator::VoxelLineIterator( const v3f &start_position, const v3f &line_vector) : diff --git a/src/voxelalgorithms.h b/src/voxelalgorithms.h index cdffe86c8..b518979d7 100644 --- a/src/voxelalgorithms.h +++ b/src/voxelalgorithms.h @@ -97,6 +97,15 @@ void update_block_border_lighting(Map *map, MapBlock *block, void blit_back_with_light(ServerMap *map, MMVManip *vm, std::map *modified_blocks); +/*! + * Corrects the light in a map block. + * For server use only. + * + * \param block the block to update + */ +void repair_block_light(ServerMap *map, MapBlock *block, + std::map *modified_blocks); + /*! * This class iterates trough voxels that intersect with * a line. The collision detection does not see nodeboxes, -- cgit v1.2.3