/* 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); }
/* Gets lighting value at face of node Parameters must consist of air and !air. Order doesn't matter. If either of the nodes doesn't exist, light is 0. parameters: daynight_ratio: 0...1000 n: getNodeParent(p) n2: getNodeParent(p + face_dir) face_dir: axis oriented unit vector from p to p2 returns encoded light value. */ u8 getFaceLight(u32 daynight_ratio, MapNode n, MapNode n2, v3s16 face_dir) { try{ u8 light; u8 l1 = n.getLightBlend(daynight_ratio); u8 l2 = n2.getLightBlend(daynight_ratio); 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 light; } catch(InvalidPositionException &e) { return 0; } }
/* 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); }
/* 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); }
/* 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 Run() { TC parent; MapBlock b(&parent, v3s16(1,1,1)); v3s16 relpos(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE); UASSERT(b.getPosRelative() == relpos); UASSERT(b.getBox().MinEdge.X == MAP_BLOCKSIZE); UASSERT(b.getBox().MaxEdge.X == MAP_BLOCKSIZE*2-1); UASSERT(b.getBox().MinEdge.Y == MAP_BLOCKSIZE); UASSERT(b.getBox().MaxEdge.Y == MAP_BLOCKSIZE*2-1); UASSERT(b.getBox().MinEdge.Z == MAP_BLOCKSIZE); UASSERT(b.getBox().MaxEdge.Z == MAP_BLOCKSIZE*2-1); UASSERT(b.isValidPosition(v3s16(0,0,0)) == true); UASSERT(b.isValidPosition(v3s16(-1,0,0)) == false); UASSERT(b.isValidPosition(v3s16(-1,-142,-2341)) == false); UASSERT(b.isValidPosition(v3s16(-124,142,2341)) == false); UASSERT(b.isValidPosition(v3s16(MAP_BLOCKSIZE-1,MAP_BLOCKSIZE-1,MAP_BLOCKSIZE-1)) == true); UASSERT(b.isValidPosition(v3s16(MAP_BLOCKSIZE-1,MAP_BLOCKSIZE,MAP_BLOCKSIZE-1)) == false); /* TODO: this method should probably be removed if the block size isn't going to be set variable */ /*UASSERT(b.getSizeNodes() == v3s16(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE));*/ // Changed flag should be initially set UASSERT(b.getModified() == MOD_STATE_WRITE_NEEDED); b.resetModified(); UASSERT(b.getModified() == MOD_STATE_CLEAN); // All nodes should have been set to // .d=CONTENT_IGNORE and .getLight() = 0 for(u16 z=0; z<MAP_BLOCKSIZE; z++) for(u16 y=0; y<MAP_BLOCKSIZE; y++) for(u16 x=0; x<MAP_BLOCKSIZE; x++) { //UASSERT(b.getNode(v3s16(x,y,z)).getContent() == CONTENT_AIR); UASSERT(b.getNode(v3s16(x,y,z)).getContent() == CONTENT_IGNORE); UASSERT(b.getNode(v3s16(x,y,z)).getLight(LIGHTBANK_DAY) == 0); UASSERT(b.getNode(v3s16(x,y,z)).getLight(LIGHTBANK_NIGHT) == 0); } { MapNode n(CONTENT_AIR); for(u16 z=0; z<MAP_BLOCKSIZE; z++) for(u16 y=0; y<MAP_BLOCKSIZE; y++) for(u16 x=0; x<MAP_BLOCKSIZE; x++) { b.setNode(v3s16(x,y,z), n); } } /* Parent fetch functions */ parent.position_valid = false; parent.node.setContent(5); MapNode n; // Positions in the block should still be valid UASSERT(b.isValidPositionParent(v3s16(0,0,0)) == true); UASSERT(b.isValidPositionParent(v3s16(MAP_BLOCKSIZE-1,MAP_BLOCKSIZE-1,MAP_BLOCKSIZE-1)) == true); n = b.getNodeParent(v3s16(0,MAP_BLOCKSIZE-1,0)); UASSERT(n.getContent() == CONTENT_AIR); // ...but outside the block they should be invalid UASSERT(b.isValidPositionParent(v3s16(-121,2341,0)) == false); UASSERT(b.isValidPositionParent(v3s16(-1,0,0)) == false); UASSERT(b.isValidPositionParent(v3s16(MAP_BLOCKSIZE-1,MAP_BLOCKSIZE-1,MAP_BLOCKSIZE)) == false); { bool exception_thrown = false; try{ // This should throw an exception MapNode n = b.getNodeParent(v3s16(0,0,-1)); } catch(InvalidPositionException &e) { exception_thrown = true; } UASSERT(exception_thrown); } parent.position_valid = true; // Now the positions outside should be valid UASSERT(b.isValidPositionParent(v3s16(-121,2341,0)) == true); UASSERT(b.isValidPositionParent(v3s16(-1,0,0)) == true); UASSERT(b.isValidPositionParent(v3s16(MAP_BLOCKSIZE-1,MAP_BLOCKSIZE-1,MAP_BLOCKSIZE)) == true); n = b.getNodeParent(v3s16(0,0,MAP_BLOCKSIZE)); UASSERT(n.getContent() == 5); /* Set a node */ v3s16 p(1,2,0); n.setContent(4); b.setNode(p, n); UASSERT(b.getNode(p).getContent() == 4); //TODO: Update to new system /*UASSERT(b.getNodeTile(p) == 4); UASSERT(b.getNodeTile(v3s16(-1,-1,0)) == 5);*/ /* propagateSunlight() */ // Set lighting of all nodes to 0 for(u16 z=0; z<MAP_BLOCKSIZE; z++){ for(u16 y=0; y<MAP_BLOCKSIZE; y++){ for(u16 x=0; x<MAP_BLOCKSIZE; x++){ MapNode n = b.getNode(v3s16(x,y,z)); n.setLight(LIGHTBANK_DAY, 0); n.setLight(LIGHTBANK_NIGHT, 0); b.setNode(v3s16(x,y,z), n); } } } { /* Check how the block handles being a lonely sky block */ parent.position_valid = true; b.setIsUnderground(false); parent.node.setContent(CONTENT_AIR); parent.node.setLight(LIGHTBANK_DAY, LIGHT_SUN); parent.node.setLight(LIGHTBANK_NIGHT, 0); core::map<v3s16, bool> light_sources; // The bottom block is invalid, because we have a shadowing node UASSERT(b.propagateSunlight(light_sources) == false); UASSERT(b.getNode(v3s16(1,4,0)).getLight(LIGHTBANK_DAY) == LIGHT_SUN); UASSERT(b.getNode(v3s16(1,3,0)).getLight(LIGHTBANK_DAY) == LIGHT_SUN); UASSERT(b.getNode(v3s16(1,2,0)).getLight(LIGHTBANK_DAY) == 0); UASSERT(b.getNode(v3s16(1,1,0)).getLight(LIGHTBANK_DAY) == 0); UASSERT(b.getNode(v3s16(1,0,0)).getLight(LIGHTBANK_DAY) == 0); UASSERT(b.getNode(v3s16(1,2,3)).getLight(LIGHTBANK_DAY) == LIGHT_SUN); UASSERT(b.getFaceLight2(1000, p, v3s16(0,1,0)) == LIGHT_SUN); UASSERT(b.getFaceLight2(1000, p, v3s16(0,-1,0)) == 0); UASSERT(b.getFaceLight2(0, p, v3s16(0,-1,0)) == 0); // According to MapBlock::getFaceLight, // The face on the z+ side should have double-diminished light //UASSERT(b.getFaceLight(p, v3s16(0,0,1)) == diminish_light(diminish_light(LIGHT_MAX))); // The face on the z+ side should have diminished light UASSERT(b.getFaceLight2(1000, p, v3s16(0,0,1)) == diminish_light(LIGHT_MAX)); } /* Check how the block handles being in between blocks with some non-sunlight while being underground */ { // Make neighbours to exist and set some non-sunlight to them parent.position_valid = true; b.setIsUnderground(true); parent.node.setLight(LIGHTBANK_DAY, LIGHT_MAX/2); core::map<v3s16, bool> light_sources; // The block below should be valid because there shouldn't be // sunlight in there either UASSERT(b.propagateSunlight(light_sources, true) == true); // Should not touch nodes that are not affected (that is, all of them) //UASSERT(b.getNode(v3s16(1,2,3)).getLight() == LIGHT_SUN); // Should set light of non-sunlighted blocks to 0. UASSERT(b.getNode(v3s16(1,2,3)).getLight(LIGHTBANK_DAY) == 0); } /* Set up a situation where: - There is only air in this block - There is a valid non-sunlighted block at the bottom, and - Invalid blocks elsewhere. - the block is not underground. This should result in bottom block invalidity */ { b.setIsUnderground(false); // Clear block for(u16 z=0; z<MAP_BLOCKSIZE; z++){ for(u16 y=0; y<MAP_BLOCKSIZE; y++){ for(u16 x=0; x<MAP_BLOCKSIZE; x++){ MapNode n; n.setContent(CONTENT_AIR); n.setLight(LIGHTBANK_DAY, 0); b.setNode(v3s16(x,y,z), n); } } } // Make neighbours invalid parent.position_valid = false; // Add exceptions to the top of the bottom block for(u16 x=0; x<MAP_BLOCKSIZE; x++) for(u16 z=0; z<MAP_BLOCKSIZE; z++) { parent.validity_exceptions.push_back(v3s16(MAP_BLOCKSIZE+x, MAP_BLOCKSIZE-1, MAP_BLOCKSIZE+z)); } // Lighting value for the valid nodes parent.node.setLight(LIGHTBANK_DAY, LIGHT_MAX/2); core::map<v3s16, bool> light_sources; // Bottom block is not valid UASSERT(b.propagateSunlight(light_sources) == false); } }
/* Lights neighbors of from_nodes, collects all them and then goes on recursively. */ void VoxelManipulator::spreadLight(enum LightBank bank, std::set<v3s16> & from_nodes, INodeDefManager *nodemgr) { 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; std::set<v3s16> lighted_nodes; for(std::set<v3s16>::iterator j = from_nodes.begin(); j != from_nodes.end(); ++j) { v3s16 pos = *j; VoxelArea voxel_area(pos - v3s16(1,1,1), pos + v3s16(1,1,1)); addArea(voxel_area); u32 i = m_area.index(pos); if(m_flags[i] & VOXELFLAG_NO_DATA) continue; MapNode &n = m_data[i]; u8 oldlight = n.getLight(bank, nodemgr); 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]; try { u32 n2i = m_area.index(n2pos); if(m_flags[n2i] & VOXELFLAG_NO_DATA) continue; MapNode &n2 = m_data[n2i]; u8 light2 = n2.getLight(bank, nodemgr); /* If the neighbor is brighter than the current node, add to list (it will light up this node on its turn) */ if(light2 > undiminish_light(oldlight)) { lighted_nodes.insert(n2pos); } /* If the neighbor is dimmer than how much light this node would spread on it, add to list */ if(light2 < newlight) { if(nodemgr->get(n2).light_propagates) { n2.setLight(bank, newlight, nodemgr); lighted_nodes.insert(n2pos); } } } catch(InvalidPositionException &e) { continue; } } } /*dstream<<"spreadLight(): Changed block " <<blockchangecount<<" times" <<" for "<<from_nodes.size()<<" nodes" <<std::endl;*/ if(!lighted_nodes.empty()) spreadLight(bank, lighted_nodes, nodemgr); }
void VoxelManipulator::spreadLight(enum LightBank bank, v3s16 p, INodeDefManager *nodemgr) { 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 }; VoxelArea voxel_area(p - v3s16(1,1,1), p + v3s16(1,1,1)); addArea(voxel_area); u32 i = m_area.index(p); if(m_flags[i] & VOXELFLAG_NO_DATA) return; MapNode &n = m_data[i]; u8 oldlight = n.getLight(bank, nodemgr); 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 = p + dirs[i]; u32 n2i = m_area.index(n2pos); if(m_flags[n2i] & VOXELFLAG_NO_DATA) continue; MapNode &n2 = m_data[n2i]; u8 light2 = n2.getLight(bank, nodemgr); /* If the neighbor is brighter than the current node, add to list (it will light up this node on its turn) */ if(light2 > undiminish_light(oldlight)) { spreadLight(bank, n2pos, nodemgr); } /* If the neighbor is dimmer than how much light this node would spread on it, add to list */ if(light2 < newlight) { if(nodemgr->get(n2).light_propagates) { n2.setLight(bank, newlight, nodemgr); spreadLight(bank, n2pos, nodemgr); } } } }
/* Lights neighbors of from_nodes, collects all them and then goes on recursively. */ void VoxelManipulator::spreadLight(enum LightBank bank, core::map<v3s16, bool> & from_nodes, INodeDefManager *nodemgr) { 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.size() == 0) return; core::map<v3s16, bool> lighted_nodes; core::map<v3s16, bool>::Iterator j; j = from_nodes.getIterator(); for(; j.atEnd() == false; j++) { v3s16 pos = j.getNode()->getKey(); emerge(VoxelArea(pos - v3s16(1,1,1), pos + v3s16(1,1,1))); u32 i = m_area.index(pos); if(m_flags[i] & VOXELFLAG_INEXISTENT) continue; MapNode &n = m_data[i]; u8 oldlight = n.getLight(bank, nodemgr); 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]; try { u32 n2i = m_area.index(n2pos); if(m_flags[n2i] & VOXELFLAG_INEXISTENT) continue; MapNode &n2 = m_data[n2i]; u8 light2 = n2.getLight(bank, nodemgr); /* If the neighbor is brighter than the current node, add to list (it will light up this node on its turn) */ if(light2 > undiminish_light(oldlight)) { lighted_nodes.insert(n2pos, true); } /* If the neighbor is dimmer than how much light this node would spread on it, add to list */ if(light2 < newlight) { if(nodemgr->get(n2).light_propagates) { n2.setLight(bank, newlight, nodemgr); lighted_nodes.insert(n2pos, true); } } } catch(InvalidPositionException &e) { continue; } } } /*dstream<<"spreadLight(): Changed block " <<blockchangecount<<" times" <<" for "<<from_nodes.size()<<" nodes" <<std::endl;*/ if(lighted_nodes.size() > 0) spreadLight(bank, lighted_nodes, nodemgr); }
SunlightPropagateResult propagateSunlight(VoxelManipulator &v, VoxelArea a, bool inexistent_top_provides_sunlight, std::set<v3s16> & light_sources, INodeDefManager *ndef) { // Return values bool bottom_sunlight_valid = true; // The full area we shall touch extends one extra at top and bottom VoxelArea required_a = a; required_a.pad(v3s16(0,1,0)); // Make sure we have access to it v.emerge(a); s16 max_y = a.MaxEdge.Y; s16 min_y = a.MinEdge.Y; for(s32 x=a.MinEdge.X; x<=a.MaxEdge.X; x++) for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++) { v3s16 p_overtop(x, max_y+1, z); bool overtop_has_sunlight = false; // If overtop node does not exist, trust heuristics if(!v.exists(p_overtop)) overtop_has_sunlight = inexistent_top_provides_sunlight; else if(v.getNodeRefUnsafe(p_overtop).getContent() == CONTENT_IGNORE) overtop_has_sunlight = inexistent_top_provides_sunlight; // Otherwise refer to it's light value else overtop_has_sunlight = (v.getNodeRefUnsafe(p_overtop).getLight( LIGHTBANK_DAY, ndef) == LIGHT_SUN); // Copy overtop's sunlight all over the place u8 incoming_light = overtop_has_sunlight ? LIGHT_SUN : 0; for(s32 y=max_y; y>=min_y; y--) { v3s16 p(x,y,z); MapNode &n = v.getNodeRefUnsafe(p); if(incoming_light == 0) { // Do nothing } else if(incoming_light == LIGHT_SUN && ndef->get(n).sunlight_propagates) { // Do nothing } else if(ndef->get(n).sunlight_propagates == false) { incoming_light = 0; } else { incoming_light = diminish_light(incoming_light); } u8 old_light = n.getLight(LIGHTBANK_DAY, ndef); if(incoming_light > old_light) n.setLight(LIGHTBANK_DAY, incoming_light, ndef); if(diminish_light(incoming_light) != 0) light_sources.insert(p); } // Check validity of sunlight at top of block below if it // hasn't already been proven invalid if(bottom_sunlight_valid) { bool sunlight_should_continue_down = (incoming_light == LIGHT_SUN); v3s16 p_overbottom(x, min_y-1, z); if(!v.exists(p_overbottom) || v.getNodeRefUnsafe(p_overbottom ).getContent() == CONTENT_IGNORE) { // Is not known, cannot compare } else { bool overbottom_has_sunlight = (v.getNodeRefUnsafe(p_overbottom ).getLight(LIGHTBANK_DAY, ndef) == LIGHT_SUN); if(sunlight_should_continue_down != overbottom_has_sunlight) { bottom_sunlight_valid = false; } } } } return SunlightPropagateResult(bottom_sunlight_valid); }
void Camera::update(LocalPlayer* player, f32 frametime, v2u32 screensize) { // Set player node transformation m_playernode->setPosition(player->getPosition()); m_playernode->setRotation(v3f(0, -1 * player->getYaw(), 0)); m_playernode->updateAbsolutePosition(); // Set head node transformation m_headnode->setPosition(player->getEyeOffset()); m_headnode->setRotation(v3f(player->getPitch(), 0, 0)); m_headnode->updateAbsolutePosition(); // Compute relative camera position and target v3f rel_cam_pos = v3f(0,0,0); v3f rel_cam_target = v3f(0,0,1); v3f rel_cam_up = v3f(0,1,0); if (m_view_bobbing_state != 0 && m_view_bobbing_anim != 0) { f32 bobfrac = my_modf(m_view_bobbing_anim * 2); f32 bobdir = (m_view_bobbing_anim < 0.5) ? 1.0 : -1.0; f32 bobknob = 1.2; f32 bobtmp = sin(pow(bobfrac, bobknob) * PI); v3f bobvec = v3f( 0.3 * bobdir * sin(bobfrac * PI), -0.28 * bobtmp * bobtmp, 0.); float f = 1.0; f *= m_view_bobbing_amount; rel_cam_pos += bobvec * f; rel_cam_target += bobvec * f; rel_cam_target.Z -= 0.005 * bobvec.Z * f; rel_cam_up.rotateXYBy(-0.03 * bobdir * bobtmp * PI * f); } // Compute absolute camera position and target m_headnode->getAbsoluteTransformation().transformVect(m_camera_position, rel_cam_pos); m_headnode->getAbsoluteTransformation().rotateVect(m_camera_direction, rel_cam_target - rel_cam_pos); v3f abs_cam_up; m_headnode->getAbsoluteTransformation().rotateVect(abs_cam_up, rel_cam_up); // Update offset if too far away from the center of the map m_camera_offset.X += CAMERA_OFFSET_STEP* (((s16)(m_camera_position.X/BS) - m_camera_offset.X)/CAMERA_OFFSET_STEP); m_camera_offset.Y += CAMERA_OFFSET_STEP* (((s16)(m_camera_position.Y/BS) - m_camera_offset.Y)/CAMERA_OFFSET_STEP); m_camera_offset.Z += CAMERA_OFFSET_STEP* (((s16)(m_camera_position.Z/BS) - m_camera_offset.Z)/CAMERA_OFFSET_STEP); // Set camera node transformation m_cameranode->setPosition(m_camera_position-intToFloat(m_camera_offset, BS)); m_cameranode->setUpVector(abs_cam_up); // *100.0 helps in large map coordinates m_cameranode->setTarget(m_camera_position-intToFloat(m_camera_offset, BS) + 100 * m_camera_direction); // FOV and aspect ratio m_aspect = (f32)screensize.X / (f32) screensize.Y; m_fov_y = m_fov * PI / 180.0; m_fov_x = 2 * atan(0.5 * m_aspect * tan(m_fov_y)); m_cameranode->setAspectRatio(m_aspect); m_cameranode->setFOV(m_fov_y); // Position the wielded item v3f wield_position = m_wieldnode_baseposition; v3f wield_rotation = m_wieldnode_baserotation; if (m_digging_button != -1) { f32 digfrac = m_digging_anim; wield_position.X -= 30 * sin(pow(digfrac, 0.8f) * PI); wield_position.Y += 15 * sin(digfrac * 2 * PI); wield_position.Z += 5 * digfrac; // Euler angles are PURE EVIL, so why not use quaternions? core::quaternion quat_begin(wield_rotation * core::DEGTORAD); core::quaternion quat_end(v3f(90, -10, -130) * core::DEGTORAD); core::quaternion quat_slerp; quat_slerp.slerp(quat_begin, quat_end, sin(digfrac * PI)); quat_slerp.toEuler(wield_rotation); wield_rotation *= core::RADTODEG; }else{ f32 bobfrac = my_modf(m_view_bobbing_anim); wield_position.X -= sin(bobfrac*PI*2.0) * 3.0; wield_position.Y += sin(my_modf(bobfrac*2.0)*PI) * 3.0; } m_wieldnode->setPosition(wield_position); m_wieldnode->setRotation(wield_rotation); u8 li = decode_light(player->light); // Set brightness one lower than incoming light diminish_light(li); m_wieldnode->updateLight(li); // Render distance feedback loop updateViewingRange(frametime); // If the player seems to be walking on solid ground, // view bobbing is enabled and free_move is off, // start (or continue) the view bobbing animation. v3f speed = player->getSpeed(); if ( (hypot(speed.X, speed.Z) > BS) && (player->touching_ground) && m_view_bobbing == true && player->control.free == false ) { // Start animation m_view_bobbing_state = 1; m_view_bobbing_speed = MYMIN(speed.getLength(), 60)*1.2; }else if (m_view_bobbing_state == 1) { // Stop animation m_view_bobbing_state = 2; m_view_bobbing_speed = 60; } }
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; }