/* Calculate non-smooth lighting at face of node. Single light bank. */ static u8 getFaceLight(enum LightBank bank, MapNode n, MapNode n2, v3s16 face_dir, MeshMakeData *data) { INodeDefManager *ndef = data->m_gamedef->ndef(); u8 light; u8 l1 = n.getLight(bank, ndef); u8 l2 = n2.getLight(bank, ndef); if(l1 > l2) light = l1; else light = l2; // Make some nice difference to different sides // This makes light come from a corner /*if(face_dir.X == 1 || face_dir.Z == 1 || face_dir.Y == -1) light = diminish_light(diminish_light(light)); else if(face_dir.X == -1 || face_dir.Z == -1) light = diminish_light(light);*/ // All neighboring faces have different shade (like in minecraft) if(face_dir.X == 1 || face_dir.X == -1 || face_dir.Y == -1) light = diminish_light(diminish_light(light)); else if(face_dir.Z == 1 || face_dir.Z == -1) light = diminish_light(light); return decode_light(light); }
void Run() { MapNode n; // Default values assert(n.getContent() == CONTENT_AIR); assert(n.getLight(LIGHTBANK_DAY) == 0); assert(n.getLight(LIGHTBANK_NIGHT) == 0); // Transparency n.setContent(CONTENT_AIR); assert(n.light_propagates() == true); n.setContent(CONTENT_STONE); assert(n.light_propagates() == false); }
/* Calculate smooth lighting at the XYZ- corner of p. Single light bank. */ static u8 getSmoothLight(enum LightBank bank, v3s16 p, MeshMakeData *data) { static v3s16 dirs8[8] = { v3s16(0,0,0), v3s16(0,0,1), v3s16(0,1,0), v3s16(0,1,1), v3s16(1,0,0), v3s16(1,1,0), v3s16(1,0,1), v3s16(1,1,1), }; INodeDefManager *ndef = data->m_gamedef->ndef(); u16 ambient_occlusion = 0; u16 light = 0; u16 light_count = 0; u8 light_source_max = 0; for(u32 i=0; i<8; i++) { MapNode n = data->m_vmanip.getNodeNoEx(p - dirs8[i]); const ContentFeatures &f = ndef->get(n); if(f.light_source > light_source_max) light_source_max = f.light_source; // Check f.solidness because fast-style leaves look // better this way if(f.param_type == CPT_LIGHT && f.solidness != 2) { light += decode_light(n.getLight(bank, ndef)); light_count++; } else if(n.getContent() != CONTENT_IGNORE) { ambient_occlusion++; } } if(light_count == 0) return 255; light /= light_count; // Boost brightness around light sources if(decode_light(light_source_max) >= light) //return decode_light(undiminish_light(light_source_max)); return decode_light(light_source_max); if(ambient_occlusion > 4) { ambient_occlusion -= 4; light = (float)light / ((float)ambient_occlusion * 0.5 + 1.0); } return light; }
/* Calculate non-smooth lighting at face of node. Single light bank. */ static u8 getFaceLight(enum LightBank bank, MapNode n, MapNode n2, v3s16 face_dir, INodeDefManager *ndef) { u8 light; u8 l1 = n.getLight(bank, ndef); u8 l2 = n2.getLight(bank, ndef); if(l1 > l2) light = l1; else light = l2; // Boost light level for light sources u8 light_source = MYMAX(ndef->get(n).light_source, ndef->get(n2).light_source); if(light_source > light) light = light_source; return decode_light(light); }
/* Calculate non-smooth lighting at face of node. Single light bank. */ static u8 getFaceLight(enum LightBank bank, MapNode n, MapNode n2, v3s16 face_dir, INodeDefManager *ndef) { u8 light; u8 l1 = n.getLight(bank, ndef); u8 l2 = n2.getLight(bank, ndef); if(l1 > l2) light = l1; else light = l2; // Boost light level for light sources u8 light_source = MYMAX(ndef->get(n).light_source, ndef->get(n2).light_source); //if(light_source >= light) //return decode_light(undiminish_light(light_source)); if(light_source > light) //return decode_light(light_source); light = light_source; // Make some nice difference to different sides // This makes light come from a corner /*if(face_dir.X == 1 || face_dir.Z == 1 || face_dir.Y == -1) light = diminish_light(diminish_light(light)); else if(face_dir.X == -1 || face_dir.Z == -1) light = diminish_light(light);*/ // All neighboring faces have different shade (like in minecraft) if(face_dir.X == 1 || face_dir.X == -1 || face_dir.Y == -1) light = diminish_light(diminish_light(light)); else if(face_dir.Z == 1 || face_dir.Z == -1) light = diminish_light(light); return decode_light(light); }
// This is probably very useless void spawnRandomObjects(MapBlock *block) { for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++) for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++) { bool last_node_walkable = false; for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++) { v3s16 p(x0,y0,z0); MapNode n = block->getNodeNoEx(p); if(n.getContent() == CONTENT_IGNORE) continue; if(content_features(n).liquid_type != LIQUID_NONE) continue; if(content_features(n).walkable) { last_node_walkable = true; continue; } if(last_node_walkable) { // If block contains light information if(content_features(n).param_type == CPT_LIGHT) { if(n.getLight(LIGHTBANK_DAY) <= 5) { if(myrand() % 1000 == 0) { v3f pos_f = intToFloat(p+block->getPosRelative(), BS); pos_f.Y -= BS*0.4; ServerActiveObject *obj = new Oerkki1SAO(NULL,0,pos_f); std::string data = obj->getStaticData(); StaticObject s_obj(obj->getType(), obj->getBasePosition(), data); // Add one block->m_static_objects.insert(0, s_obj); delete obj; block->setChangedFlag(); } } } } last_node_walkable = false; } } }
/* Calculate non-smooth lighting at interior of node. Single light bank. */ static u8 getInteriorLight(enum LightBank bank, MapNode n, s32 increment, INodeDefManager *ndef) { u8 light = n.getLight(bank, ndef); while(increment > 0) { light = undiminish_light(light); --increment; } while(increment < 0) { light = diminish_light(light); ++increment; } return decode_light(light); }
/* Calculate non-smooth lighting at interior of node. Single light bank. */ static u8 getInteriorLight(enum LightBank bank, MapNode n, s32 increment, MeshMakeData *data) { INodeDefManager *ndef = data->m_gamedef->ndef(); u8 light = n.getLight(bank, ndef); while(increment > 0) { light = undiminish_light(light); --increment; } while(increment < 0) { light = diminish_light(light); ++increment; } return decode_light(light); }
static bool getVisibleBrightness(Map *map, v3f p0, v3f dir, float step, float step_multiplier, float start_distance, float end_distance, INodeDefManager *ndef, u32 daylight_factor, float sunlight_min_d, int *result, bool *sunlight_seen) { int brightness_sum = 0; int brightness_count = 0; float distance = start_distance; dir.normalize(); v3f pf = p0; pf += dir * distance; int noncount = 0; bool nonlight_seen = false; bool allow_allowing_non_sunlight_propagates = false; bool allow_non_sunlight_propagates = false; // Check content nearly at camera position { v3s16 p = floatToInt(p0 /*+ dir * 3*BS*/, BS); MapNode n = map->getNodeNoEx(p); if(ndef->get(n).param_type == CPT_LIGHT && !ndef->get(n).sunlight_propagates) allow_allowing_non_sunlight_propagates = true; } // If would start at CONTENT_IGNORE, start closer { v3s16 p = floatToInt(pf, BS); MapNode n = map->getNodeNoEx(p); if(n.getContent() == CONTENT_IGNORE){ float newd = 2*BS; pf = p0 + dir * 2*newd; distance = newd; sunlight_min_d = 0; } } for(int i=0; distance < end_distance; i++){ pf += dir * step; distance += step; step *= step_multiplier; v3s16 p = floatToInt(pf, BS); MapNode n = map->getNodeNoEx(p); if(allow_allowing_non_sunlight_propagates && i == 0 && ndef->get(n).param_type == CPT_LIGHT && !ndef->get(n).sunlight_propagates){ allow_non_sunlight_propagates = true; } if(ndef->get(n).param_type != CPT_LIGHT || (!ndef->get(n).sunlight_propagates && !allow_non_sunlight_propagates)){ nonlight_seen = true; noncount++; if(noncount >= 4) break; continue; } if(distance >= sunlight_min_d && *sunlight_seen == false && nonlight_seen == false) if(n.getLight(LIGHTBANK_DAY, ndef) == LIGHT_SUN) *sunlight_seen = true; noncount = 0; brightness_sum += decode_light(n.getLightBlend(daylight_factor, ndef)); brightness_count++; } *result = 0; if(brightness_count == 0) return false; *result = brightness_sum / brightness_count; /*std::cerr<<"Sampled "<<brightness_count<<" points; result=" <<(*result)<<std::endl;*/ return true; }
/* Propagates sunlight down through the block. Doesn't modify nodes that are not affected by sunlight. Returns false if sunlight at bottom block is invalid. Returns true if sunlight at bottom block is valid. Returns true if bottom block doesn't exist. If there is a block above, continues from it. If there is no block above, assumes there is sunlight, unless is_underground is set or highest node is water. All sunlighted nodes are added to light_sources. if remove_light==true, sets non-sunlighted nodes black. if black_air_left!=NULL, it is set to true if non-sunlighted air is left in block. */ bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources, bool remove_light, bool *black_air_left) { INodeDefManager *nodemgr = m_gamedef->ndef(); // Whether the sunlight at the top of the bottom block is valid bool block_below_is_valid = true; v3s16 pos_relative = getPosRelative(); for(s16 x=0; x<MAP_BLOCKSIZE; x++) { for(s16 z=0; z<MAP_BLOCKSIZE; z++) { #if 1 bool no_sunlight = false; bool no_top_block = false; // Check if node above block has sunlight try{ MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z)); if(n.getContent() == CONTENT_IGNORE) { // Trust heuristics no_sunlight = is_underground; } else if(n.getLight(LIGHTBANK_DAY, m_gamedef->ndef()) != LIGHT_SUN) { no_sunlight = true; } } catch(InvalidPositionException &e) { no_top_block = true; // NOTE: This makes over-ground roofed places sunlighted // Assume sunlight, unless is_underground==true if(is_underground) { no_sunlight = true; } else { MapNode n = getNode(v3s16(x, MAP_BLOCKSIZE-1, z)); if(m_gamedef->ndef()->get(n).sunlight_propagates == false) { no_sunlight = true; } } // NOTE: As of now, this just would make everything dark. // No sunlight here //no_sunlight = true; } #endif #if 0 // Doesn't work; nothing gets light. bool no_sunlight = true; bool no_top_block = false; // Check if node above block has sunlight try{ MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z)); if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN) { no_sunlight = false; } } catch(InvalidPositionException &e) { no_top_block = true; } #endif /*std::cout<<"("<<x<<","<<z<<"): " <<"no_top_block="<<no_top_block <<", is_underground="<<is_underground <<", no_sunlight="<<no_sunlight <<std::endl;*/ s16 y = MAP_BLOCKSIZE-1; // This makes difference to diminishing in water. bool stopped_to_solid_object = false; u8 current_light = no_sunlight ? 0 : LIGHT_SUN; for(; y >= 0; y--) { v3s16 pos(x, y, z); MapNode &n = getNodeRef(pos); if(current_light == 0) { // Do nothing } else if(current_light == LIGHT_SUN && nodemgr->get(n).sunlight_propagates) { // Do nothing: Sunlight is continued } else if(nodemgr->get(n).light_propagates == false) { // A solid object is on the way. stopped_to_solid_object = true; // Light stops. current_light = 0; } else { // Diminish light current_light = diminish_light(current_light); } u8 old_light = n.getLight(LIGHTBANK_DAY, nodemgr); if(current_light > old_light || remove_light) { n.setLight(LIGHTBANK_DAY, current_light, nodemgr); } if(diminish_light(current_light) != 0) { light_sources.insert(pos_relative + pos, true); } if(current_light == 0 && stopped_to_solid_object) { if(black_air_left) { *black_air_left = true; } } } // Whether or not the block below should see LIGHT_SUN bool sunlight_should_go_down = (current_light == LIGHT_SUN); /* If the block below hasn't already been marked invalid: Check if the node below the block has proper sunlight at top. If not, the block below is invalid. Ignore non-transparent nodes as they always have no light */ try { if(block_below_is_valid) { MapNode n = getNodeParent(v3s16(x, -1, z)); if(nodemgr->get(n).light_propagates) { if(n.getLight(LIGHTBANK_DAY, nodemgr) == LIGHT_SUN && sunlight_should_go_down == false) block_below_is_valid = false; else if(n.getLight(LIGHTBANK_DAY, nodemgr) != LIGHT_SUN && sunlight_should_go_down == true) block_below_is_valid = false; } }//if }//try catch(InvalidPositionException &e) { /*std::cout<<"InvalidBlockException for bottom block node" <<std::endl;*/ // Just no block below, no need to panic. } } } return block_below_is_valid; }
void VoxelManipulator::print(std::ostream &o, INodeDefManager *ndef, VoxelPrintMode mode) { v3s16 em = m_area.getExtent(); v3s16 of = m_area.MinEdge; o<<"size: "<<em.X<<"x"<<em.Y<<"x"<<em.Z <<" offset: ("<<of.X<<","<<of.Y<<","<<of.Z<<")"<<std::endl; for(s32 y=m_area.MaxEdge.Y; y>=m_area.MinEdge.Y; y--) { if(em.X >= 3 && em.Y >= 3) { if (y==m_area.MinEdge.Y+2) o<<"^ "; else if(y==m_area.MinEdge.Y+1) o<<"| "; else if(y==m_area.MinEdge.Y+0) o<<"y x-> "; else o<<" "; } for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++) { for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++) { u8 f = m_flags[m_area.index(x,y,z)]; char c; if(f & VOXELFLAG_NO_DATA) c = 'N'; else { c = 'X'; MapNode n = m_data[m_area.index(x,y,z)]; content_t m = n.getContent(); u8 pr = n.param2; if(mode == VOXELPRINT_MATERIAL) { if(m <= 9) c = m + '0'; } else if(mode == VOXELPRINT_WATERPRESSURE) { if(ndef->get(m).isLiquid()) { c = 'w'; if(pr <= 9) c = pr + '0'; } else if(m == CONTENT_AIR) { c = ' '; } else { c = '#'; } } else if(mode == VOXELPRINT_LIGHT_DAY) { if(ndef->get(m).light_source != 0) c = 'S'; else if(ndef->get(m).light_propagates == false) c = 'X'; else { u8 light = n.getLight(LIGHTBANK_DAY, ndef); if(light < 10) c = '0' + light; else c = 'a' + (light-10); } } } o<<c; } o<<' '; } o<<std::endl; } }
/* Calculate smooth lighting at the XYZ- corner of p. Both light banks */ static u16 getSmoothLightCombined(v3s16 p, MeshMakeData *data) { static const v3s16 dirs8[8] = { v3s16(0,0,0), v3s16(0,0,1), v3s16(0,1,0), v3s16(0,1,1), v3s16(1,0,0), v3s16(1,1,0), v3s16(1,0,1), v3s16(1,1,1), }; INodeDefManager *ndef = data->m_gamedef->ndef(); u16 ambient_occlusion = 0; u16 light_count = 0; u8 light_source_max = 0; u16 light_day = 0; u16 light_night = 0; for (u32 i = 0; i < 8; i++) { MapNode n = data->m_vmanip.getNodeNoEx(p - dirs8[i]); // if it's CONTENT_IGNORE we can't do any light calculations if (n.getContent() == CONTENT_IGNORE) { continue; } const ContentFeatures &f = ndef->get(n); if (f.light_source > light_source_max) light_source_max = f.light_source; // Check f.solidness because fast-style leaves look better this way if (f.param_type == CPT_LIGHT && f.solidness != 2) { light_day += decode_light(n.getLight(LIGHTBANK_DAY, ndef)); light_night += decode_light(n.getLight(LIGHTBANK_NIGHT, ndef)); light_count++; } else { ambient_occlusion++; } } if(light_count == 0) return 0xffff; light_day /= light_count; light_night /= light_count; // Boost brightness around light sources bool skip_ambient_occlusion_day = false; if(decode_light(light_source_max) >= light_day) { light_day = decode_light(light_source_max); skip_ambient_occlusion_day = true; } bool skip_ambient_occlusion_night = false; if(decode_light(light_source_max) >= light_night) { light_night = decode_light(light_source_max); skip_ambient_occlusion_night = true; } if (ambient_occlusion > 4) { //table of precalculated gamma space multiply factors //light^2.2 * factor (0.75, 0.5, 0.25, 0.0), so table holds factor ^ (1 / 2.2) static const float light_amount[4] = { 0.877424315, 0.729740053, 0.532520545, 0.0 }; //calculate table index for gamma space multiplier ambient_occlusion -= 5; if (!skip_ambient_occlusion_day) light_day = rangelim(core::round32(light_day*light_amount[ambient_occlusion]), 0, 255); if (!skip_ambient_occlusion_night) light_night = rangelim(core::round32(light_night*light_amount[ambient_occlusion]), 0, 255); } return light_day | (light_night << 8); }
/* Calculate smooth lighting at the XYZ- corner of p. Single light bank. */ static u8 getSmoothLight(enum LightBank bank, v3s16 p, MeshMakeData *data) { static v3s16 dirs8[8] = { v3s16(0,0,0), v3s16(0,0,1), v3s16(0,1,0), v3s16(0,1,1), v3s16(1,0,0), v3s16(1,1,0), v3s16(1,0,1), v3s16(1,1,1), }; INodeDefManager *ndef = data->m_gamedef->ndef(); u16 ambient_occlusion = 0; u16 light = 0; u16 light_count = 0; u8 light_source_max = 0; for(u32 i=0; i<8; i++) { MapNode n = data->m_vmanip.getNodeNoEx(p - dirs8[i]); // if it's CONTENT_IGNORE we can't do any light calculations if (n.getContent() == CONTENT_IGNORE) { continue; } const ContentFeatures &f = ndef->get(n); if(f.light_source > light_source_max) light_source_max = f.light_source; // Check f.solidness because fast-style leaves look // better this way if(f.param_type == CPT_LIGHT && f.solidness != 2) { light += decode_light(n.getLight(bank, ndef)); light_count++; } else { ambient_occlusion++; } } if(light_count == 0) return 255; light /= light_count; // Boost brightness around light sources if(decode_light(light_source_max) >= light) //return decode_light(undiminish_light(light_source_max)); return decode_light(light_source_max); if(ambient_occlusion > 4) { //ambient_occlusion -= 4; //light = (float)light / ((float)ambient_occlusion * 0.5 + 1.0); float light_amount = (8 - ambient_occlusion) / 4.0; float light_f = (float)light / 255.0; light_f = pow(light_f, 2.2f); // gamma -> linear space light_f = light_f * light_amount; light_f = pow(light_f, 1.0f/2.2f); // linear -> gamma space if(light_f > 1.0) light_f = 1.0; light = 255.0 * light_f + 0.5; } return light; }
bool Map::propagateSunlight(v3POS pos, std::set<v3POS> & light_sources, bool remove_light) { MapBlock *block = getBlockNoCreateNoEx(pos); INodeDefManager *nodemgr = m_gamedef->ndef(); // Whether the sunlight at the top of the bottom block is valid bool block_below_is_valid = true; v3POS pos_relative = block->getPosRelative(); for(s16 x = 0; x < MAP_BLOCKSIZE; ++x) { for(s16 z = 0; z < MAP_BLOCKSIZE; ++z) { bool no_sunlight = false; // Check if node above block has sunlight MapNode n = getNode(pos_relative + v3POS(x, MAP_BLOCKSIZE, z)); if (n) { if(n.getLight(LIGHTBANK_DAY, m_gamedef->ndef()) != LIGHT_SUN) { no_sunlight = true; } } else { // NOTE: This makes over-ground roofed places sunlighted // Assume sunlight, unless is_underground==true if(block->getIsUnderground()) { no_sunlight = true; } else { MapNode n = block->getNode(v3POS(x, MAP_BLOCKSIZE - 1, z)); if(n && m_gamedef->ndef()->get(n).sunlight_propagates == false) no_sunlight = true; } // NOTE: As of now, this just would make everything dark. // No sunlight here //no_sunlight = true; } s16 y = MAP_BLOCKSIZE - 1; // This makes difference to diminishing in water. //bool stopped_to_solid_object = false; u8 current_light = no_sunlight ? 0 : LIGHT_SUN; for(; y >= 0; --y) { v3POS pos(x, y, z); MapNode n = block->getNode(pos); if(current_light == 0) { // Do nothing } else if(current_light == LIGHT_SUN && nodemgr->get(n).sunlight_propagates) { // Do nothing: Sunlight is continued } else if(nodemgr->get(n).light_propagates == false) { // A solid object is on the way. //stopped_to_solid_object = true; // Light stops. current_light = 0; } else { // Diminish light current_light = diminish_light(current_light); } u8 old_light = n.getLight(LIGHTBANK_DAY, nodemgr); if(current_light > old_light || remove_light) { n.setLight(LIGHTBANK_DAY, current_light, nodemgr); block->setNode(pos, n); } if(diminish_light(current_light) != 0) { light_sources.insert(pos_relative + pos); } } // Whether or not the block below should see LIGHT_SUN bool sunlight_should_go_down = (current_light == LIGHT_SUN); /* If the block below hasn't already been marked invalid: Check if the node below the block has proper sunlight at top. If not, the block below is invalid. Ignore non-transparent nodes as they always have no light */ if(block_below_is_valid) { MapNode n = getNode(pos_relative + v3POS(x, -1, z)); if (n) { if(nodemgr->get(n).light_propagates) { if(n.getLight(LIGHTBANK_DAY, nodemgr) == LIGHT_SUN && sunlight_should_go_down == false) block_below_is_valid = false; else if(n.getLight(LIGHTBANK_DAY, nodemgr) != LIGHT_SUN && sunlight_should_go_down == true) block_below_is_valid = false; } } else { // Just no block below, no need to panic. } } } } return block_below_is_valid; }
u32 Map::updateLighting(concurrent_map<v3POS, MapBlock*> & a_blocks, std::map<v3POS, MapBlock*> & modified_blocks, unsigned int max_cycle_ms) { INodeDefManager *nodemgr = m_gamedef->ndef(); int ret = 0; int loopcount = 0; TimeTaker timer("updateLighting"); // For debugging //bool debug=true; //u32 count_was = modified_blocks.size(); //std::unordered_set<v3POS, v3POSHash, v3POSEqual> light_sources; //std::unordered_map<v3POS, u8, v3POSHash, v3POSEqual> unlight_from_day, unlight_from_night; std::set<v3POS> light_sources; std::map<v3POS, u8> unlight_from_day, unlight_from_night; unordered_map_v3POS<int> processed; int num_bottom_invalid = 0; //MutexAutoLock lock2(m_update_lighting_mutex); #if !ENABLE_THREADS auto lock = m_nothread_locker.lock_unique_rec(); #endif { //TimeTaker t("updateLighting: first stuff"); u32 end_ms = porting::getTimeMs() + max_cycle_ms; for(auto i = a_blocks.begin(); i != a_blocks.end(); ++i) { auto block = getBlockNoCreateNoEx(i->first); for(;;) { // Don't bother with dummy blocks. if(!block || block->isDummy()) break; auto lock = block->try_lock_unique_rec(); if (!lock->owns_lock()) break; // may cause dark areas v3POS pos = block->getPos(); if (processed.count(pos) && processed[pos] <= i->first.Y ) { break; } ++loopcount; processed[pos] = i->first.Y; v3POS posnodes = block->getPosRelative(); //modified_blocks[pos] = block; block->setLightingExpired(true); block->lighting_broken = true; /* 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++) { v3POS 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_day = n.getLight(LIGHTBANK_DAY, nodemgr); u8 oldlight_night = n.getLight(LIGHTBANK_NIGHT, nodemgr); n.setLight(LIGHTBANK_DAY, 0, nodemgr); n.setLight(LIGHTBANK_NIGHT, 0, nodemgr); block->setNode(p, n); // If node sources light, add to list //u8 source = nodemgr->get(n).light_source; if(nodemgr->get(n).light_source) light_sources.insert(p + posnodes); v3POS p_map = 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) { if(oldlight_day) unlight_from_day[p_map] = oldlight_day; if(oldlight_night) unlight_from_night[p_map] = oldlight_night; } } lock->unlock(); bool bottom_valid = propagateSunlight(pos, light_sources); if(!bottom_valid) num_bottom_invalid++; pos.Y--; block = getBlockNoCreateNoEx(pos); } if (porting::getTimeMs() > end_ms) { ++ret; break; } } } { //TimeTaker timer("updateLighting: unspreadLight"); unspreadLight(LIGHTBANK_DAY, unlight_from_day, light_sources, modified_blocks); unspreadLight(LIGHTBANK_NIGHT, unlight_from_night, light_sources, modified_blocks); } { //TimeTaker timer("updateLighting: spreadLight"); spreadLight(LIGHTBANK_DAY, light_sources, modified_blocks, porting::getTimeMs() + max_cycle_ms * 10); spreadLight(LIGHTBANK_NIGHT, light_sources, modified_blocks, porting::getTimeMs() + max_cycle_ms * 10); } for (auto & i : processed) { a_blocks.erase(i.first); MapBlock *block = getBlockNoCreateNoEx(i.first); if(!block) continue; block->setLightingExpired(false); block->lighting_broken = false; } g_profiler->add("Server: light blocks", loopcount); return ret; }