]> git.lizzy.rs Git - dragonfireclient.git/commitdiff
Light calculation: New bulk node lighting code
authorDániel Juhász <juhdanad@gmail.com>
Sat, 10 Dec 2016 18:02:44 +0000 (19:02 +0100)
committerparamat <mat.gregory@virginmedia.com>
Sat, 11 Mar 2017 02:06:18 +0000 (02:06 +0000)
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.

doc/lua_api.txt
src/map.cpp
src/map.h
src/mg_schematic.cpp
src/mg_schematic.h
src/script/lua_api/l_mapgen.cpp
src/script/lua_api/l_vmanip.cpp
src/treegen.cpp
src/voxelalgorithms.cpp
src/voxelalgorithms.h

index 23aac90d9d0a2796298d14e2f50c11685ad2ca7d..484a5848c586c99b2f78d5d708c77ecd6369a4bc 100644 (file)
@@ -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
 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
 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.
 * 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()`.
-* `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
 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
     * 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
-* `update_map()`: Update map after writing chunk back to map.
-    * To be used only by `VoxelManip` objects created by the mod itself;
-      not a `VoxelManip` that was retrieved from `minetest.get_mapgen_object`
+* `update_map()`: Does nothing, kept for compatibility.
 * `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>}`
     * To be used only by a `VoxelManip` object from `minetest.get_mapgen_object`
index a415bda9618111c109d5617194a98aca81451519..a1502befa5ff0a7aa161b062dae82c340ce01d2d 100644 (file)
@@ -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)
index 19c94ee80b013e7bb4e43d793794e37112504468..c4181a49f48791dcda8b18e0d012fcf079e37af7 100644 (file)
--- a/src/map.h
+++ b/src/map.h
@@ -208,22 +208,6 @@ class Map /*: public NodeContainer*/
        // position is valid, otherwise false
        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.
        */
index 3d08d86fa13cc5398cf98ab347c804134066fdee..92e138df4ba4a5f8eb4353f2c27c17e6c9ea446f 100644 (file)
@@ -30,6 +30,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "util/serialize.h"
 #include "serialization.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)));
 }
 
-void Schematic::placeOnMap(Map *map, v3s16 p, u32 flags,
+void Schematic::placeOnMap(ServerMap *map, v3s16 p, u32 flags,
        Rotation rot, bool force_place)
 {
        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);
 
-       vm.blitBackAll(&modified_blocks);
+       voxalgo::blit_back_with_light(map, &vm, &modified_blocks);
 
        //// 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
        MapEditEvent event;
        event.type = MEET_OTHER;
index 1d46e6ac4fc7dfa78ead419b91efb215a5fc160c..2f60c843b00c57b9f49a47e1edf6644cd5cbb3f7 100644 (file)
@@ -25,6 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "util/string.h"
 
 class Map;
+class ServerMap;
 class Mapgen;
 class MMVManip;
 class PseudoRandom;
@@ -108,7 +109,7 @@ class Schematic : public ObjDef, public NodeResolver {
 
        void blitToVManip(MMVManip *vm, v3s16 p, 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,
                std::vector<std::pair<v3s16, u8> > *plist,
index bc1c32f030c15a42a464c6171fc2fdf68048d3c2..0bc9e25f7cd3d26456241b1e0b3877dddcd91d5f 100644 (file)
@@ -1357,7 +1357,9 @@ int ModApiMapgen::l_place_schematic(lua_State *L)
 {
        MAP_LOCK_REQUIRED;
 
-       Map *map = &(getEnv(L)->getMap());
+       GET_ENV_PTR;
+
+       ServerMap *map = &(env->getServerMap());
        SchematicManager *schemmgr = getServer(L)->getEmergeManager()->schemmgr;
 
        //// Read position
index bdf720f0a10f5f480e53787787a451f11a65d201..5f129d2afd7d0bc8ea79f88318f8c9ec75829f8b 100644 (file)
@@ -27,6 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "map.h"
 #include "server.h"
 #include "mapgen.h"
+#include "voxelalgorithms.h"
 
 // garbage collector
 int LuaVoxelManip::gc_object(lua_State *L)
@@ -109,10 +110,24 @@ int LuaVoxelManip::l_write_to_map(lua_State *L)
        MAP_LOCK_REQUIRED;
 
        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;
 }
 
@@ -322,33 +337,6 @@ int LuaVoxelManip::l_set_param2_data(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;
 }
 
index 4df574f349307915a9033e15c805bb29752135b9..505954e8e07545ca923457e30977c5ddb03dfb50 100644 (file)
@@ -25,6 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "serverenvironment.h"
 #include "nodedef.h"
 #include "treegen.h"
+#include "voxelalgorithms.h"
 
 namespace treegen
 {
@@ -125,12 +126,8 @@ treegen::error spawn_ltree(ServerEnvironment *env, v3s16 p0,
        if (e != SUCCESS)
                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
        MapEditEvent event;
        event.type = MEET_OTHER;
index 3c32bc125f0b313f3cc73f1b6f9629c52f9c6fbb..411369bd4aade263a68cf0faad2b02f39bbceb55 100644 (file)
@@ -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
  * node above it.
@@ -753,7 +768,7 @@ void update_lighting_nodes(Map *map,
                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++) {
+                                       it < lights.end(); ++it) {
                                MapNode n = it->block->getNodeNoCheck(it->rel_position,
                                        &is_valid_position);
                                n.setLight(bank, i, ndef);
@@ -817,8 +832,10 @@ void update_block_border_lighting(Map *map, MapBlock *block,
        bool is_valid_position;
        for (s32 i = 0; i < 2; i++) {
                LightBank bank = banks[i];
-               UnlightQueue disappearing_lights(256);
-               ReLightQueue light_sources(256);
+               // Since invalid light is not common, do not allocate
+               // memory if not needed.
+               UnlightQueue disappearing_lights(0);
+               ReLightQueue light_sources(0);
                // Get incorrect lights
                for (direction d = 0; d < 6; d++) {
                        // For each direction
@@ -873,7 +890,7 @@ void update_block_border_lighting(Map *map, MapBlock *block,
                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++) {
+                                       it < lights.end(); ++it) {
                                MapNode n = it->block->getNodeNoCheck(it->rel_position,
                                        &is_valid_position);
                                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(
        const v3f &start_position,
        const v3f &line_vector) :
index bf1638fa36f57c9f6a3aa7cb48551e10e6c980cb..cdffe86c8d6284572a71730c9e624dfd33841c9a 100644 (file)
@@ -26,7 +26,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "util/cpp11_container.h"
 
 class Map;
+class ServerMap;
 class MapBlock;
+class MMVManip;
 
 namespace voxalgo
 {
@@ -84,6 +86,17 @@ void update_lighting_nodes(
 void update_block_border_lighting(Map *map, MapBlock *block,
        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
  * a line. The collision detection does not see nodeboxes,