]> git.lizzy.rs Git - dragonfireclient.git/commitdiff
Light update for map blocks
authorDániel Juhász <juhdanad@gmail.com>
Sat, 11 Mar 2017 16:07:04 +0000 (17:07 +0100)
committerEkdohibs <nathanael.courant@laposte.net>
Thu, 20 Apr 2017 03:39:14 +0000 (05:39 +0200)
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.

doc/lua_api.txt
src/map.cpp
src/map.h
src/script/lua_api/l_env.cpp
src/script/lua_api/l_env.h
src/script/lua_api/l_vmanip.cpp
src/voxelalgorithms.cpp
src/voxelalgorithms.h

index 6e7a1de681b37de1fc97a05fb4183ce1bb1fb852..16e662e0c069580634b3d68003a0eaaab551c44b 100644 (file)
@@ -2398,6 +2398,22 @@ and `minetest.auth_reload` call the authetification handler.
     * increase level of leveled node by level, default `level` equals `1`
     * if `totallevel > maxlevel`, returns rest (`total-max`)
     * can be negative for decreasing
+* `minetest.fix_light(pos1, pos2)`: returns `true`/`false`
+    * resets the light in a cuboid-shaped part of
+      the map and removes lighting bugs.
+    * Loads the area if it is not loaded.
+    * `pos1` is the corner of the cuboid with the least coordinates
+      (in node coordinates), inclusive.
+    * `pos2` is the opposite corner of the cuboid, inclusive.
+    * The actual updated cuboid might be larger than the specified one,
+      because only whole map blocks can be updated.
+      The actual updated area consists of those map blocks that intersect
+      with the given cuboid.
+    * However, the neighborhood of the updated area might change
+      as well, as light can spread out of the cuboid, also light
+      might be removed.
+    * returns `false` if the area is not fully generated,
+      `true` otherwise
 * `core.check_single_for_falling(pos)`
     * causes an unsupported `group:falling_node` node to fall and causes an
       unattached `group:attached_node` node to fall.
@@ -3421,8 +3437,14 @@ will place the schematic inside of the VoxelManip.
 * `read_from_map(p1, p2)`:  Loads a chunk of map into the VoxelManip object containing
   the region formed by `p1` and `p2`.
     * returns actual emerged `pmin`, actual emerged `pmax`
-* `write_to_map()`: Writes the data loaded from the `VoxelManip` back to the map.
+* `write_to_map([light])`: Writes the data loaded from the `VoxelManip` back to the map.
     * **important**: data must be set using `VoxelManip:set_data()` before calling this
+    * if `light` is true, then lighting is automatically recalculated.
+      The default value is true.
+      If `light` is false, no light calculations happen, and you should correct
+      all modified blocks with `minetest.fix_light()` as soon as possible.
+      Keep in mind that modifying the map where light is incorrect can cause
+      more lighting bugs. 
 * `get_node_at(pos)`: Returns a `MapNode` table of the node currently loaded in
   the `VoxelManip` at that position
 * `set_node_at(pos, node)`: Sets a specific `MapNode` in the `VoxelManip` at that position
index f8bbee180710acd621b90e3d65776ded50d848f7..8754813dd685d90d8610ed772d0c18ca9973d16b 100644 (file)
@@ -2591,6 +2591,16 @@ void ServerMap::PrintInfo(std::ostream &out)
        out<<"ServerMap: ";
 }
 
+bool ServerMap::repairBlockLight(v3s16 blockpos,
+       std::map<v3s16, MapBlock *> *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),
index 744a4d1e2db28b76fa143e4ee5e2a07de16f3779..739cdb59b5ec448cbe1f8ba9f7800873b4e9d591 100644 (file)
--- a/src/map.h
+++ b/src/map.h
@@ -477,6 +477,16 @@ class ServerMap : public Map
        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<v3s16, MapBlock *> *modified_blocks);
+
        MapSettingsManager settings_mgr;
 
 private:
index 4fad7b37ced316915696970c3be88f071d966bdc..1fa7845b596a226cbff0df26e739c8483b027386 100644 (file)
@@ -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<v3s16, MapBlock *> 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<v3s16, MapBlock *>::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);
index 38b2282d7cf7e59eee34eb54dda06cc6ba6daa2b..3f688b398326cc08013e854e806ecd3e430002b7 100644 (file)
@@ -128,6 +128,9 @@ class ModApiEnvMod : public ModApiBase {
        // 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);
 
index 7316fb200e6f078f1aa6f1ec0addd0be3dcb5743..254a7e5a64fbfaf92455576d3aefeeb11a3d445a 100644 (file)
@@ -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,
index f2142717fad3e6073e2a841cca71d514d9e3cffc..40f8595a752144a7e0ec25f5aa9b52d52ccfaf9a 100644 (file)
@@ -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<v3s16, MapBlock*> *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) :
index cdffe86c8d6284572a71730c9e624dfd33841c9a..b518979d7f095a4a7c92009c4886c901250833dc 100644 (file)
@@ -97,6 +97,15 @@ void update_block_border_lighting(Map *map, MapBlock *block,
 void blit_back_with_light(ServerMap *map, MMVManip *vm,
        std::map<v3s16, MapBlock*> *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<v3s16, MapBlock*> *modified_blocks);
+
 /*!
  * This class iterates trough voxels that intersect with
  * a line. The collision detection does not see nodeboxes,