]> git.lizzy.rs Git - dragonfireclient.git/blobdiff - src/mapblock.cpp
Moved the temporary mapgen test files and added a modified map.cpp too... These are...
[dragonfireclient.git] / src / mapblock.cpp
index 40af0e397170353d5772ec796d334eb03a5037b9..58b23bd146e6001d50055a18357c1e1ae308bde7 100644 (file)
@@ -19,80 +19,212 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 
 #include "mapblock.h"
 #include "map.h"
-// For g_materials
+// For g_settings
 #include "main.h"
 #include "light.h"
 #include <sstream>
 
+#ifndef SERVER
+void MeshMakeData::fill(u32 daynight_ratio, MapBlock *block)
+{
+       m_daynight_ratio = daynight_ratio;
+       m_blockpos = block->getPos();
 
-/*
-       MapBlock
-*/
+       v3s16 blockpos_nodes = m_blockpos*MAP_BLOCKSIZE;
+       
+       /*
+               There is no harm not copying the TempMods of the neighbors
+               because they are already copied to this block
+       */
+       m_temp_mods.clear();
+       block->copyTempMods(m_temp_mods);
+       
+       /*
+               Copy data
+       */
+
+       // Allocate this block + neighbors
+       m_vmanip.clear();
+       m_vmanip.addArea(VoxelArea(blockpos_nodes-v3s16(1,1,1)*MAP_BLOCKSIZE,
+                       blockpos_nodes+v3s16(1,1,1)*MAP_BLOCKSIZE*2-v3s16(1,1,1)));
 
-bool MapBlock::isValidPositionParent(v3s16 p)
-{
-       if(isValidPosition(p))
        {
-               return true;
+               //TimeTaker timer("copy central block data");
+               // 0ms
+
+               // Copy our data
+               block->copyTo(m_vmanip);
        }
-       else{
-               return m_parent->isValidPosition(getPosRelative() + p);
+       {
+               //TimeTaker timer("copy neighbor block data");
+               // 0ms
+
+               /*
+                       Copy neighbors. This is lightning fast.
+                       Copying only the borders would be *very* slow.
+               */
+               
+               // Get map
+               NodeContainer *parentcontainer = block->getParent();
+               // This will only work if the parent is the map
+               assert(parentcontainer->nodeContainerId() == NODECONTAINER_ID_MAP);
+               // OK, we have the map!
+               Map *map = (Map*)parentcontainer;
+
+               for(u16 i=0; i<6; i++)
+               {
+                       const v3s16 &dir = g_6dirs[i];
+                       v3s16 bp = m_blockpos + dir;
+                       MapBlock *b = map->getBlockNoCreateNoEx(bp);
+                       if(b)
+                               b->copyTo(m_vmanip);
+               }
        }
 }
+#endif
 
-MapNode MapBlock::getNodeParent(v3s16 p)
+/*
+       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)
 {
-       if(isValidPosition(p) == false)
-       {
-               return m_parent->getNode(getPosRelative() + p);
+       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;
        }
-       else
+       catch(InvalidPositionException &e)
        {
-               if(data == NULL)
-                       throw InvalidPositionException();
-               return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
+               return 0;
        }
 }
 
-void MapBlock::setNodeParent(v3s16 p, MapNode & n)
+#ifndef SERVER
+
+/*
+       vertex_dirs: v3s16[4]
+*/
+void getNodeVertexDirs(v3s16 dir, v3s16 *vertex_dirs)
 {
-       if(isValidPosition(p) == false)
+       /*
+               If looked from outside the node towards the face, the corners are:
+               0: bottom-right
+               1: bottom-left
+               2: top-left
+               3: top-right
+       */
+       if(dir == v3s16(0,0,1))
        {
-               m_parent->setNode(getPosRelative() + p, n);
+               // If looking towards z+, this is the face that is behind
+               // the center point, facing towards z+.
+               vertex_dirs[0] = v3s16(-1,-1, 1);
+               vertex_dirs[1] = v3s16( 1,-1, 1);
+               vertex_dirs[2] = v3s16( 1, 1, 1);
+               vertex_dirs[3] = v3s16(-1, 1, 1);
        }
-       else
+       else if(dir == v3s16(0,0,-1))
        {
-               if(data == NULL)
-                       throw InvalidPositionException();
-               data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X] = n;
+               // faces towards Z-
+               vertex_dirs[0] = v3s16( 1,-1,-1);
+               vertex_dirs[1] = v3s16(-1,-1,-1);
+               vertex_dirs[2] = v3s16(-1, 1,-1);
+               vertex_dirs[3] = v3s16( 1, 1,-1);
+       }
+       else if(dir == v3s16(1,0,0))
+       {
+               // faces towards X+
+               vertex_dirs[0] = v3s16( 1,-1, 1);
+               vertex_dirs[1] = v3s16( 1,-1,-1);
+               vertex_dirs[2] = v3s16( 1, 1,-1);
+               vertex_dirs[3] = v3s16( 1, 1, 1);
+       }
+       else if(dir == v3s16(-1,0,0))
+       {
+               // faces towards X-
+               vertex_dirs[0] = v3s16(-1,-1,-1);
+               vertex_dirs[1] = v3s16(-1,-1, 1);
+               vertex_dirs[2] = v3s16(-1, 1, 1);
+               vertex_dirs[3] = v3s16(-1, 1,-1);
+       }
+       else if(dir == v3s16(0,1,0))
+       {
+               // faces towards Y+ (assume Z- as "down" in texture)
+               vertex_dirs[0] = v3s16( 1, 1,-1);
+               vertex_dirs[1] = v3s16(-1, 1,-1);
+               vertex_dirs[2] = v3s16(-1, 1, 1);
+               vertex_dirs[3] = v3s16( 1, 1, 1);
+       }
+       else if(dir == v3s16(0,-1,0))
+       {
+               // faces towards Y- (assume Z+ as "down" in texture)
+               vertex_dirs[0] = v3s16( 1,-1, 1);
+               vertex_dirs[1] = v3s16(-1,-1, 1);
+               vertex_dirs[2] = v3s16(-1,-1,-1);
+               vertex_dirs[3] = v3s16( 1,-1,-1);
        }
 }
 
-FastFace * MapBlock::makeFastFace(u16 tile, u8 light, v3f p,
-               v3f dir, v3f scale, v3f posRelative_f)
+inline video::SColor lightColor(u8 alpha, u8 light)
 {
-       FastFace *f = new FastFace;
+       return video::SColor(alpha,light,light,light);
+}
+
+void makeFastFace(TileSpec tile, u8 li0, u8 li1, u8 li2, u8 li3, v3f p,
+               v3s16 dir, v3f scale, v3f posRelative_f,
+               core::array<FastFace> &dest)
+{
+       FastFace face;
        
        // Position is at the center of the cube.
        v3f pos = p * BS;
        posRelative_f *= BS;
 
        v3f vertex_pos[4];
-       // If looking towards z+, this is the face that is behind
-       // the center point, facing towards z+.
-       vertex_pos[0] = v3f( BS/2,-BS/2,BS/2);
-       vertex_pos[1] = v3f(-BS/2,-BS/2,BS/2);
-       vertex_pos[2] = v3f(-BS/2, BS/2,BS/2);
-       vertex_pos[3] = v3f( BS/2, BS/2,BS/2);
-       
-       /*
-               TODO: Rotate it the right way (one side comes upside down)
-       */
-       core::CMatrix4<f32> m;
-       m.buildRotateFromTo(v3f(0,0,1), dir);
-       
-       for(u16 i=0; i<4; i++){
-               m.rotateVect(vertex_pos[i]);
+       v3s16 vertex_dirs[4];
+       getNodeVertexDirs(dir, vertex_dirs);
+       for(u16 i=0; i<4; i++)
+       {
+               vertex_pos[i] = v3f(
+                               BS/2*vertex_dirs[i].X,
+                               BS/2*vertex_dirs[i].Y,
+                               BS/2*vertex_dirs[i].Z
+               );
+       }
+
+       for(u16 i=0; i<4; i++)
+       {
                vertex_pos[i].X *= scale.X;
                vertex_pos[i].Y *= scale.Y;
                vertex_pos[i].Z *= scale.Z;
@@ -106,115 +238,266 @@ FastFace * MapBlock::makeFastFace(u16 tile, u8 light, v3f p,
 
        v3f zerovector = v3f(0,0,0);
        
-       u8 li = decode_light(light);
-       //u8 li = 150;
-
-       u8 alpha = 255;
-
-       //if(material == CONTENT_WATER || material == CONTENT_OCEAN)
-       if(tile == CONTENT_WATER || tile == CONTENT_OCEAN)
-       //if(tile == TILE_WATER)
-       {
-               alpha = 128;
-       }
-
-       video::SColor c = video::SColor(alpha,li,li,li);
-
-       /*f->vertices[0] = video::S3DVertex(vertex_pos[0], zerovector, c,
-                       core::vector2d<f32>(0,1));
-       f->vertices[1] = video::S3DVertex(vertex_pos[1], zerovector, c,
-                       core::vector2d<f32>(abs_scale,1));
-       f->vertices[2] = video::S3DVertex(vertex_pos[2], zerovector, c,
-                       core::vector2d<f32>(abs_scale,0));
-       f->vertices[3] = video::S3DVertex(vertex_pos[3], zerovector, c,
-                       core::vector2d<f32>(0,0));*/
-       f->vertices[0] = video::S3DVertex(vertex_pos[0], zerovector, c,
-                       core::vector2d<f32>(0,1));
-       f->vertices[1] = video::S3DVertex(vertex_pos[1], zerovector, c,
-                       core::vector2d<f32>(abs_scale,1));
-       f->vertices[2] = video::S3DVertex(vertex_pos[2], zerovector, c,
-                       core::vector2d<f32>(abs_scale,0));
-       f->vertices[3] = video::S3DVertex(vertex_pos[3], zerovector, c,
-                       core::vector2d<f32>(0,0));
-
-       f->tile = tile;
+       u8 alpha = tile.alpha;
+       /*u8 alpha = 255;
+       if(tile.id == TILE_WATER)
+               alpha = WATER_ALPHA;*/
+
+       float x0 = tile.texture.pos.X;
+       float y0 = tile.texture.pos.Y;
+       float w = tile.texture.size.X;
+       float h = tile.texture.size.Y;
+
+       /*video::SColor c = lightColor(alpha, li);
+
+       face.vertices[0] = video::S3DVertex(vertex_pos[0], v3f(0,1,0), c,
+                       core::vector2d<f32>(x0+w*abs_scale, y0+h));
+       face.vertices[1] = video::S3DVertex(vertex_pos[1], v3f(0,1,0), c,
+                       core::vector2d<f32>(x0, y0+h));
+       face.vertices[2] = video::S3DVertex(vertex_pos[2], v3f(0,1,0), c,
+                       core::vector2d<f32>(x0, y0));
+       face.vertices[3] = video::S3DVertex(vertex_pos[3], v3f(0,1,0), c,
+                       core::vector2d<f32>(x0+w*abs_scale, y0));*/
+
+       face.vertices[0] = video::S3DVertex(vertex_pos[0], v3f(0,1,0),
+                       lightColor(alpha, li0),
+                       core::vector2d<f32>(x0+w*abs_scale, y0+h));
+       face.vertices[1] = video::S3DVertex(vertex_pos[1], v3f(0,1,0),
+                       lightColor(alpha, li1),
+                       core::vector2d<f32>(x0, y0+h));
+       face.vertices[2] = video::S3DVertex(vertex_pos[2], v3f(0,1,0),
+                       lightColor(alpha, li2),
+                       core::vector2d<f32>(x0, y0));
+       face.vertices[3] = video::S3DVertex(vertex_pos[3], v3f(0,1,0),
+                       lightColor(alpha, li3),
+                       core::vector2d<f32>(x0+w*abs_scale, y0));
+
+       face.tile = tile;
        //DEBUG
-       //f->tile = TILE_GRASS;
-
-       return f;
+       //f->tile = TILE_STONE;
+       
+       dest.push_back(face);
 }
        
 /*
-       Parameters must consist of air and !air.
-       Order doesn't matter.
-
-       If either of the nodes doesn't exist, light is 0.
+       Gets node tile from any place relative to block.
+       Returns TILE_NODE if doesn't exist or should not be drawn.
 */
-u8 MapBlock::getFaceLight(v3s16 p, v3s16 face_dir)
+TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 face_dir,
+               NodeModMap &temp_mods)
 {
-       try{
-               MapNode n = getNodeParent(p);
-               MapNode n2 = getNodeParent(p + face_dir);
-               u8 light;
-               /*if(n.solidness() < n2.solidness())
-                       light = n.getLight();
-               else
-                       light = n2.getLight();*/
-               if(n.getLight() > n2.getLight())
-                       light = n.getLight();
-               else
-                       light = n2.getLight();
-
-               // Make some nice difference to different sides
+       TileSpec spec;
+       spec = mn.getTile(face_dir);
+       
+       /*
+               Check temporary modifications on this node
+       */
+       /*core::map<v3s16, NodeMod>::Node *n;
+       n = m_temp_mods.find(p);
+       // If modified
+       if(n != NULL)
+       {
+               struct NodeMod mod = n->getValue();*/
+       NodeMod mod;
+       if(temp_mods.get(p, &mod))
+       {
+               if(mod.type == NODEMOD_CHANGECONTENT)
+               {
+                       MapNode mn2(mod.param);
+                       spec = mn2.getTile(face_dir);
+               }
+               if(mod.type == NODEMOD_CRACK)
+               {
+                       /*
+                               Get texture id, translate it to name, append stuff to
+                               name, get texture id
+                       */
 
-               /*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);*/
+                       // Get original texture name
+                       u32 orig_id = spec.texture.id;
+                       std::string orig_name = g_texturesource->getTextureName(orig_id);
 
-               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);
+                       // Create new texture name
+                       std::ostringstream os;
+                       os<<orig_name<<"^[crack"<<mod.param;
 
-               return light;
+                       // Get new texture
+                       u32 new_id = g_texturesource->getTextureId(os.str());
+                       
+                       /*dstream<<"MapBlock::getNodeTile(): Switching from "
+                                       <<orig_name<<" to "<<os.str()<<" ("
+                                       <<orig_id<<" to "<<new_id<<")"<<std::endl;*/
+                       
+                       spec.texture = g_texturesource->getTexture(new_id);
+               }
        }
-       catch(InvalidPositionException &e)
+       
+       return spec;
+}
+
+u8 getNodeContent(v3s16 p, MapNode mn, NodeModMap &temp_mods)
+{
+       /*
+               Check temporary modifications on this node
+       */
+       /*core::map<v3s16, NodeMod>::Node *n;
+       n = m_temp_mods.find(p);
+       // If modified
+       if(n != NULL)
        {
-               return 0;
+               struct NodeMod mod = n->getValue();*/
+       NodeMod mod;
+       if(temp_mods.get(p, &mod))
+       {
+               if(mod.type == NODEMOD_CHANGECONTENT)
+               {
+                       // Overrides content
+                       return mod.param;
+               }
+               if(mod.type == NODEMOD_CRACK)
+               {
+                       /*
+                               Content doesn't change.
+                               
+                               face_contents works just like it should, because
+                               there should not be faces between differently cracked
+                               nodes.
+
+                               If a semi-transparent node is cracked in front an
+                               another one, it really doesn't matter whether there
+                               is a cracked face drawn in between or not.
+                       */
+               }
        }
+
+       return mn.d;
 }
 
-/*
-       Gets node tile from any place relative to block.
-       Returns CONTENT_IGNORE if doesn't exist or should not be drawn.
-*/
-u16 MapBlock::getNodeTile(v3s16 p, v3s16 face_dir)
+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),
+};
+
+// Calculate lighting at the XYZ- corner of p
+u8 getSmoothLight(v3s16 p, VoxelManipulator &vmanip, u32 daynight_ratio)
 {
-       try{
-               MapNode n = getNodeParent(p);
-               
-               //return content_tile(n.d);
-               return n.getTile(face_dir);
+       u16 ambient_occlusion = 0;
+       u16 light = 0;
+       u16 light_count = 0;
+       for(u32 i=0; i<8; i++)
+       {
+               MapNode n = vmanip.getNodeNoEx(p - dirs8[i]);
+               if(content_features(n.d).param_type == CPT_LIGHT)
+               {
+                       light += decode_light(n.getLightBlend(daynight_ratio));
+                       light_count++;
+               }
+               else
+               {
+                       if(n.d != CONTENT_IGNORE)
+                               ambient_occlusion++;
+               }
        }
-       catch(InvalidPositionException &e)
+
+       if(light_count == 0)
+               return 255;
+       
+       light /= light_count;
+
+       if(ambient_occlusion > 4)
        {
-               //return CONTENT_IGNORE;
-               return TILE_NONE;
+               ambient_occlusion -= 4;
+               light = (float)light / ((float)ambient_occlusion * 0.5 + 1.0);
        }
+
+       return light;
 }
 
-u8 MapBlock::getNodeContent(v3s16 p)
+// Calculate lighting at the given corner of p
+u8 getSmoothLight(v3s16 p, v3s16 corner,
+               VoxelManipulator &vmanip, u32 daynight_ratio)
 {
-       try{
-               MapNode n = getNodeParent(p);
-               
-               return n.d;
+       if(corner.X == 1) p.X += 1;
+       else              assert(corner.X == -1);
+       if(corner.Y == 1) p.Y += 1;
+       else              assert(corner.Y == -1);
+       if(corner.Z == 1) p.Z += 1;
+       else              assert(corner.Z == -1);
+       
+       return getSmoothLight(p, vmanip, daynight_ratio);
+}
+
+void getTileInfo(
+               // Input:
+               v3s16 blockpos_nodes,
+               v3s16 p,
+               v3s16 face_dir,
+               u32 daynight_ratio,
+               VoxelManipulator &vmanip,
+               NodeModMap &temp_mods,
+               bool smooth_lighting,
+               // Output:
+               bool &makes_face,
+               v3s16 &p_corrected,
+               v3s16 &face_dir_corrected,
+               u8 *lights,
+               TileSpec &tile
+       )
+{
+       MapNode n0 = vmanip.getNodeNoEx(blockpos_nodes + p);
+       MapNode n1 = vmanip.getNodeNoEx(blockpos_nodes + p + face_dir);
+       TileSpec tile0 = getNodeTile(n0, p, face_dir, temp_mods);
+       TileSpec tile1 = getNodeTile(n1, p + face_dir, -face_dir, temp_mods);
+       
+       // This is hackish
+       u8 content0 = getNodeContent(p, n0, temp_mods);
+       u8 content1 = getNodeContent(p + face_dir, n1, temp_mods);
+       u8 mf = face_contents(content0, content1);
+
+       if(mf == 0)
+       {
+               makes_face = false;
+               return;
        }
-       catch(InvalidPositionException &e)
+
+       makes_face = true;
+       
+       if(mf == 1)
+       {
+               tile = tile0;
+               p_corrected = p;
+               face_dir_corrected = face_dir;
+       }
+       else
+       {
+               tile = tile1;
+               p_corrected = p + face_dir;
+               face_dir_corrected = -face_dir;
+       }
+       
+       if(smooth_lighting == false)
+       {
+               lights[0] = lights[1] = lights[2] = lights[3] =
+                               decode_light(getFaceLight(daynight_ratio, n0, n1, face_dir));
+       }
+       else
        {
-               return CONTENT_IGNORE;
+               v3s16 vertex_dirs[4];
+               getNodeVertexDirs(face_dir_corrected, vertex_dirs);
+               for(u16 i=0; i<4; i++)
+               {
+                       lights[i] = getSmoothLight(blockpos_nodes + p_corrected,
+                                       vertex_dirs[i], vmanip, daynight_ratio);
+               }
        }
+       
+       return;
 }
 
 /*
@@ -222,51 +505,67 @@ u8 MapBlock::getNodeContent(v3s16 p)
        translate_dir: unit vector with only one of x, y or z
        face_dir: unit vector with only one of x, y or z
 */
-void MapBlock::updateFastFaceRow(v3s16 startpos,
+void updateFastFaceRow(
+               u32 daynight_ratio,
+               v3f posRelative_f,
+               v3s16 startpos,
                u16 length,
                v3s16 translate_dir,
+               v3f translate_dir_f,
                v3s16 face_dir,
-               core::list<FastFace*> &dest)
+               v3f face_dir_f,
+               core::array<FastFace> &dest,
+               NodeModMap &temp_mods,
+               VoxelManipulator &vmanip,
+               v3s16 blockpos_nodes,
+               bool smooth_lighting)
 {
-       /*
-               Precalculate some variables
-       */
-       v3f translate_dir_f(translate_dir.X, translate_dir.Y,
-                       translate_dir.Z); // floating point conversion
-       v3f face_dir_f(face_dir.X, face_dir.Y,
-                       face_dir.Z); // floating point conversion
-       v3f posRelative_f(getPosRelative().X, getPosRelative().Y,
-                       getPosRelative().Z); // floating point conversion
-
        v3s16 p = startpos;
-       /*
-               Get face light at starting position
-       */
-       u8 light = getFaceLight(p, face_dir);
        
        u16 continuous_tiles_count = 0;
        
-       u8 tile0 = getNodeTile(p, face_dir);
-       u8 tile1 = getNodeTile(p + face_dir, -face_dir);
-               
+       bool makes_face;
+       v3s16 p_corrected;
+       v3s16 face_dir_corrected;
+       u8 lights[4];
+       TileSpec tile;
+       getTileInfo(blockpos_nodes, p, face_dir, daynight_ratio,
+                       vmanip, temp_mods, smooth_lighting,
+                       makes_face, p_corrected, face_dir_corrected, lights, tile);
+
        for(u16 j=0; j<length; j++)
        {
+               // If tiling can be done, this is set to false in the next step
                bool next_is_different = true;
                
                v3s16 p_next;
-               u8 tile0_next = 0;
-               u8 tile1_next = 0;
-               u8 light_next = 0;
-
-               if(j != length - 1){
+               
+               bool next_makes_face;
+               v3s16 next_p_corrected;
+               v3s16 next_face_dir_corrected;
+               u8 next_lights[4];
+               TileSpec next_tile;
+               
+               // If at last position, there is nothing to compare to and
+               // the face must be drawn anyway
+               if(j != length - 1)
+               {
                        p_next = p + translate_dir;
-                       tile0_next = getNodeTile(p_next, face_dir);
-                       tile1_next = getNodeTile(p_next + face_dir, -face_dir);
-                       light_next = getFaceLight(p_next, face_dir);
-
-                       if(tile0_next == tile0
-                                       && tile1_next == tile1
-                                       && light_next == light)
+                       
+                       getTileInfo(blockpos_nodes, p_next, face_dir, daynight_ratio,
+                                       vmanip, temp_mods, smooth_lighting,
+                                       next_makes_face, next_p_corrected,
+                                       next_face_dir_corrected, next_lights,
+                                       next_tile);
+                       
+                       if(next_makes_face == makes_face
+                                       && next_p_corrected == p_corrected
+                                       && next_face_dir_corrected == face_dir_corrected
+                                       && next_lights[0] == lights[0]
+                                       && next_lights[1] == lights[1]
+                                       && next_lights[2] == lights[2]
+                                       && next_lights[3] == lights[3]
+                                       && next_tile == tile)
                        {
                                next_is_different = false;
                        }
@@ -274,57 +573,63 @@ void MapBlock::updateFastFaceRow(v3s16 startpos,
 
                continuous_tiles_count++;
                
-               if(next_is_different)
+               // This is set to true if the texture doesn't allow more tiling
+               bool end_of_texture = false;
+               /*
+                       If there is no texture, it can be tiled infinitely.
+                       If tiled==0, it means the texture can be tiled infinitely.
+                       Otherwise check tiled agains continuous_tiles_count.
+               */
+               if(tile.texture.atlas != NULL && tile.texture.tiled != 0)
+               {
+                       if(tile.texture.tiled <= continuous_tiles_count)
+                               end_of_texture = true;
+               }
+               
+               // Do this to disable tiling textures
+               //end_of_texture = true; //DEBUG
+               
+               if(next_is_different || end_of_texture)
                {
                        /*
                                Create a face if there should be one
                        */
-                       //u8 mf = face_contents(tile0, tile1);
-                       // This is hackish
-                       u8 content0 = getNodeContent(p);
-                       u8 content1 = getNodeContent(p + face_dir);
-                       u8 mf = face_contents(content0, content1);
-                       
-                       if(mf != 0)
+                       if(makes_face)
                        {
                                // Floating point conversion of the position vector
-                               v3f pf(p.X, p.Y, p.Z);
+                               v3f pf(p_corrected.X, p_corrected.Y, p_corrected.Z);
                                // Center point of face (kind of)
                                v3f sp = pf - ((f32)continuous_tiles_count / 2. - 0.5) * translate_dir_f;
                                v3f scale(1,1,1);
-                               if(translate_dir.X != 0){
+
+                               if(translate_dir.X != 0)
+                               {
                                        scale.X = continuous_tiles_count;
                                }
-                               if(translate_dir.Y != 0){
+                               if(translate_dir.Y != 0)
+                               {
                                        scale.Y = continuous_tiles_count;
                                }
-                               if(translate_dir.Z != 0){
+                               if(translate_dir.Z != 0)
+                               {
                                        scale.Z = continuous_tiles_count;
                                }
                                
-                               FastFace *f;
-
-                               // If node at sp (tile0) is more solid
-                               if(mf == 1)
-                               {
-                                       f = makeFastFace(tile0, light,
-                                                       sp, face_dir_f, scale,
-                                                       posRelative_f);
-                               }
-                               // If node at sp is less solid (mf == 2)
-                               else
-                               {
-                                       f = makeFastFace(tile1, light,
-                                                       sp+face_dir_f, -1*face_dir_f, scale,
-                                                       posRelative_f);
-                               }
-                               dest.push_back(f);
+                               makeFastFace(tile, lights[0], lights[1], lights[2], lights[3],
+                                               sp, face_dir_corrected, scale,
+                                               posRelative_f, dest);
                        }
 
                        continuous_tiles_count = 0;
-                       tile0 = tile0_next;
-                       tile1 = tile1_next;
-                       light = light_next;
+                       
+                       makes_face = next_makes_face;
+                       p_corrected = next_p_corrected;
+                       face_dir_corrected = next_face_dir_corrected;
+                       lights[0] = next_lights[0];
+                       lights[1] = next_lights[1];
+                       lights[2] = next_lights[2];
+                       lights[3] = next_lights[3];
+                       tile = next_tile;
                }
                
                p = p_next;
@@ -424,139 +729,205 @@ class MeshCollector
        core::array<PreMeshBuffer> m_prebuffers;
 };
 
-void MapBlock::updateMesh()
+scene::SMesh* makeMapBlockMesh(MeshMakeData *data)
 {
-       /*v3s16 p = getPosRelative();
-       std::cout<<"MapBlock("<<p.X<<","<<p.Y<<","<<p.Z<<")"
-                       <<"::updateMesh(): ";*/
-                       //<<"::updateMesh()"<<std::endl;
+       // 4-21ms for MAP_BLOCKSIZE=16
+       // 24-155ms for MAP_BLOCKSIZE=32
+       //TimeTaker timer1("makeMapBlockMesh()");
+
+       core::array<FastFace> fastfaces_new;
+
+       v3s16 blockpos_nodes = data->m_blockpos*MAP_BLOCKSIZE;
+       
+       // floating point conversion
+       v3f posRelative_f(blockpos_nodes.X, blockpos_nodes.Y, blockpos_nodes.Z);
        
        /*
-               TODO: Change this to directly generate the mesh (and get rid
-                     of FastFaces)
+               Some settings
        */
-
-       core::list<FastFace*> *fastfaces_new = new core::list<FastFace*>;
+       bool new_style_water = g_settings.getBool("new_style_water");
+       bool new_style_leaves = g_settings.getBool("new_style_leaves");
+       bool smooth_lighting = g_settings.getBool("smooth_lighting");
+       
+       float node_water_level = 1.0;
+       if(new_style_water)
+               node_water_level = 0.85;
        
        /*
                We are including the faces of the trailing edges of the block.
                This means that when something changes, the caller must
                also update the meshes of the blocks at the leading edges.
-       */
 
-       /*
-               Go through every y,z and get top faces in rows of x+
-       */
-       for(s16 y=0; y<MAP_BLOCKSIZE; y++){
-       //for(s16 y=-1; y<MAP_BLOCKSIZE; y++){
-               for(s16 z=0; z<MAP_BLOCKSIZE; z++){
-                       updateFastFaceRow(v3s16(0,y,z), MAP_BLOCKSIZE,
-                                       v3s16(1,0,0),
-                                       v3s16(0,1,0),
-                                       *fastfaces_new);
-               }
-       }
-       /*
-               Go through every x,y and get right faces in rows of z+
+               NOTE: This is the slowest part of this method.
        */
-       for(s16 x=0; x<MAP_BLOCKSIZE; x++){
-       //for(s16 x=-1; x<MAP_BLOCKSIZE; x++){
+       
+       {
+               // 4-23ms for MAP_BLOCKSIZE=16
+               //TimeTaker timer2("updateMesh() collect");
+
+               /*
+                       Go through every y,z and get top(y+) faces in rows of x+
+               */
                for(s16 y=0; y<MAP_BLOCKSIZE; y++){
-                       updateFastFaceRow(v3s16(x,y,0), MAP_BLOCKSIZE,
-                                       v3s16(0,0,1),
-                                       v3s16(1,0,0),
-                                       *fastfaces_new);
+                       for(s16 z=0; z<MAP_BLOCKSIZE; z++){
+                               updateFastFaceRow(data->m_daynight_ratio, posRelative_f,
+                                               v3s16(0,y,z), MAP_BLOCKSIZE,
+                                               v3s16(1,0,0), //dir
+                                               v3f  (1,0,0),
+                                               v3s16(0,1,0), //face dir
+                                               v3f  (0,1,0),
+                                               fastfaces_new,
+                                               data->m_temp_mods,
+                                               data->m_vmanip,
+                                               blockpos_nodes,
+                                               smooth_lighting);
+                       }
+               }
+               /*
+                       Go through every x,y and get right(x+) faces in rows of z+
+               */
+               for(s16 x=0; x<MAP_BLOCKSIZE; x++){
+                       for(s16 y=0; y<MAP_BLOCKSIZE; y++){
+                               updateFastFaceRow(data->m_daynight_ratio, posRelative_f,
+                                               v3s16(x,y,0), MAP_BLOCKSIZE,
+                                               v3s16(0,0,1),
+                                               v3f  (0,0,1),
+                                               v3s16(1,0,0),
+                                               v3f  (1,0,0),
+                                               fastfaces_new,
+                                               data->m_temp_mods,
+                                               data->m_vmanip,
+                                               blockpos_nodes,
+                                               smooth_lighting);
+                       }
+               }
+               /*
+                       Go through every y,z and get back(z+) faces in rows of x+
+               */
+               for(s16 z=0; z<MAP_BLOCKSIZE; z++){
+                       for(s16 y=0; y<MAP_BLOCKSIZE; y++){
+                               updateFastFaceRow(data->m_daynight_ratio, posRelative_f,
+                                               v3s16(0,y,z), MAP_BLOCKSIZE,
+                                               v3s16(1,0,0),
+                                               v3f  (1,0,0),
+                                               v3s16(0,0,1),
+                                               v3f  (0,0,1),
+                                               fastfaces_new,
+                                               data->m_temp_mods,
+                                               data->m_vmanip,
+                                               blockpos_nodes,
+                                               smooth_lighting);
+                       }
                }
        }
+
+       // End of slow part
+
        /*
-               Go through every y,z and get back faces in rows of x+
+               Convert FastFaces to SMesh
        */
-       for(s16 z=0; z<MAP_BLOCKSIZE; z++){
-       //for(s16 z=-1; z<MAP_BLOCKSIZE; z++){
-               for(s16 y=0; y<MAP_BLOCKSIZE; y++){
-                       updateFastFaceRow(v3s16(0,y,z), MAP_BLOCKSIZE,
-                                       v3s16(1,0,0),
-                                       v3s16(0,0,1),
-                                       *fastfaces_new);
-               }
-       }
 
-       scene::SMesh *mesh_new = NULL;
-       
-       mesh_new = new scene::SMesh();
-       
-       if(fastfaces_new->getSize() > 0)
+       MeshCollector collector;
+
+       if(fastfaces_new.size() > 0)
        {
-               MeshCollector collector;
+               // avg 0ms (100ms spikes when loading textures the first time)
+               //TimeTaker timer2("updateMesh() mesh building");
 
-               core::list<FastFace*>::Iterator i = fastfaces_new->begin();
+               video::SMaterial material;
+               material.setFlag(video::EMF_LIGHTING, false);
+               material.setFlag(video::EMF_BILINEAR_FILTER, false);
+               material.setFlag(video::EMF_FOG_ENABLE, true);
+               //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_OFF);
+               //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_SIMPLE);
 
-               for(; i != fastfaces_new->end(); i++)
+               for(u32 i=0; i<fastfaces_new.size(); i++)
                {
-                       FastFace *f = *i;
+                       FastFace &f = fastfaces_new[i];
 
                        const u16 indices[] = {0,1,2,2,3,0};
+                       const u16 indices_alternate[] = {0,1,3,2,3,1};
+                       
+                       video::ITexture *texture = f.tile.texture.atlas;
+                       if(texture == NULL)
+                               continue;
 
-                       /*collector.append(g_materials[f->material], f->vertices, 4,
-                                       indices, 6);*/
-                       /*collector.append(g_materials[f->tile], f->vertices, 4,
-                                       indices, 6);*/
-                       collector.append(g_tile_materials[f->tile], f->vertices, 4,
-                                       indices, 6);
-               }
-
-               collector.fillMesh(mesh_new);
-
-               // Use VBO for mesh (this just would set this for ever buffer)
-               //mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
-               
-               /*std::cout<<"MapBlock has "<<fastfaces_new->getSize()<<" faces "
-                               <<"and uses "<<mesh_new->getMeshBufferCount()
-                               <<" materials (meshbuffers)"<<std::endl;*/
-       }
-       
-       /*
-               Clear temporary FastFaces
-       */
+                       material.setTexture(0, texture);
+                       
+                       f.tile.applyMaterialOptions(material);
 
-       core::list<FastFace*>::Iterator i;
-       i = fastfaces_new->begin();
-       for(; i != fastfaces_new->end(); i++)
-       {
-               delete *i;
+                       const u16 *indices_p = indices;
+                       
+                       /*
+                               Revert triangles for nicer looking gradient if vertices
+                               1 and 3 have same color or 0 and 2 have different color.
+                       */
+                       if(f.vertices[0].Color != f.vertices[2].Color
+                                       || f.vertices[1].Color == f.vertices[3].Color)
+                               indices_p = indices_alternate;
+                       
+                       collector.append(material, f.vertices, 4, indices_p, 6);
+               }
        }
-       fastfaces_new->clear();
-       delete fastfaces_new;
 
        /*
                Add special graphics:
                - torches
-               
-               TODO: Optimize by using same meshbuffer for same textures
+               - flowing water
        */
 
-       /*scene::ISceneManager *smgr = NULL;
-       video::IVideoDriver* driver = NULL;
-       if(g_device)
-       {
-               smgr = g_device->getSceneManager();
-               driver = smgr->getVideoDriver();
-       }*/
-                       
+       // 0ms
+       //TimeTaker timer2("updateMesh() adding special stuff");
+
+       // Flowing water material
+       video::SMaterial material_water1;
+       material_water1.setFlag(video::EMF_LIGHTING, false);
+       material_water1.setFlag(video::EMF_BACK_FACE_CULLING, false);
+       material_water1.setFlag(video::EMF_BILINEAR_FILTER, false);
+       material_water1.setFlag(video::EMF_FOG_ENABLE, true);
+       material_water1.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
+       AtlasPointer pa_water1 = g_texturesource->getTexture(
+                       g_texturesource->getTextureId("water.png"));
+       material_water1.setTexture(0, pa_water1.atlas);
+
+       // New-style leaves material
+       video::SMaterial material_leaves1;
+       material_leaves1.setFlag(video::EMF_LIGHTING, false);
+       //material_leaves1.setFlag(video::EMF_BACK_FACE_CULLING, false);
+       material_leaves1.setFlag(video::EMF_BILINEAR_FILTER, false);
+       material_leaves1.setFlag(video::EMF_FOG_ENABLE, true);
+       material_leaves1.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
+       AtlasPointer pa_leaves1 = g_texturesource->getTexture(
+                       g_texturesource->getTextureId("leaves.png"));
+       material_leaves1.setTexture(0, pa_leaves1.atlas);
+
+       // Glass material
+       video::SMaterial material_glass;
+       material_glass.setFlag(video::EMF_LIGHTING, false);
+       material_glass.setFlag(video::EMF_BILINEAR_FILTER, false);
+       material_glass.setFlag(video::EMF_FOG_ENABLE, true);
+       material_glass.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
+       AtlasPointer pa_glass = g_texturesource->getTexture(
+                       g_texturesource->getTextureId("glass.png"));
+       material_glass.setTexture(0, pa_glass.atlas);
+
+
        for(s16 z=0; z<MAP_BLOCKSIZE; z++)
        for(s16 y=0; y<MAP_BLOCKSIZE; y++)
        for(s16 x=0; x<MAP_BLOCKSIZE; x++)
        {
                v3s16 p(x,y,z);
 
-               MapNode &n = getNodeRef(x,y,z);
+               MapNode n = data->m_vmanip.getNodeNoEx(blockpos_nodes+p);
                
-               if(n.d == CONTENT_LIGHT)
+               /*
+                       Add torches to mesh
+               */
+               if(n.d == CONTENT_TORCH)
                {
-                       //scene::IMeshBuffer *buf = new scene::SMeshBuffer();
-                       scene::SMeshBuffer *buf = new scene::SMeshBuffer();
                        video::SColor c(255,255,255,255);
 
+                       // Wall at X+ of node
                        video::S3DVertex vertices[4] =
                        {
                                video::S3DVertex(-BS/2,-BS/2,0, 0,0,0, c, 0,1),
@@ -582,63 +953,737 @@ void MapBlock::updateMesh()
                                if(dir == v3s16(0,1,0))
                                        vertices[i].Pos.rotateXZBy(-45);
 
-                               vertices[i].Pos += intToFloat(p + getPosRelative());
-                       }
+                               vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
+                       }
+
+                       // Set material
+                       video::SMaterial material;
+                       material.setFlag(video::EMF_LIGHTING, false);
+                       material.setFlag(video::EMF_BACK_FACE_CULLING, false);
+                       material.setFlag(video::EMF_BILINEAR_FILTER, false);
+                       //material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
+                       material.MaterialType
+                                       = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
+
+                       if(dir == v3s16(0,-1,0))
+                               material.setTexture(0,
+                                               g_texturesource->getTextureRaw("torch_on_floor.png"));
+                       else if(dir == v3s16(0,1,0))
+                               material.setTexture(0,
+                                               g_texturesource->getTextureRaw("torch_on_ceiling.png"));
+                       // For backwards compatibility
+                       else if(dir == v3s16(0,0,0))
+                               material.setTexture(0,
+                                               g_texturesource->getTextureRaw("torch_on_floor.png"));
+                       else
+                               material.setTexture(0, 
+                                               g_texturesource->getTextureRaw("torch.png"));
+
+                       u16 indices[] = {0,1,2,2,3,0};
+                       // Add to mesh collector
+                       collector.append(material, vertices, 4, indices, 6);
+               }
+               /*
+                       Signs on walls
+               */
+               if(n.d == CONTENT_SIGN_WALL)
+               {
+                       u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio));
+                       video::SColor c(255,l,l,l);
+                               
+                       float d = (float)BS/16;
+                       // Wall at X+ of node
+                       video::S3DVertex vertices[4] =
+                       {
+                               video::S3DVertex(BS/2-d,-BS/2,-BS/2, 0,0,0, c, 0,1),
+                               video::S3DVertex(BS/2-d,-BS/2,BS/2, 0,0,0, c, 1,1),
+                               video::S3DVertex(BS/2-d,BS/2,BS/2, 0,0,0, c, 1,0),
+                               video::S3DVertex(BS/2-d,BS/2,-BS/2, 0,0,0, c, 0,0),
+                       };
+
+                       v3s16 dir = unpackDir(n.dir);
+
+                       for(s32 i=0; i<4; i++)
+                       {
+                               if(dir == v3s16(1,0,0))
+                                       vertices[i].Pos.rotateXZBy(0);
+                               if(dir == v3s16(-1,0,0))
+                                       vertices[i].Pos.rotateXZBy(180);
+                               if(dir == v3s16(0,0,1))
+                                       vertices[i].Pos.rotateXZBy(90);
+                               if(dir == v3s16(0,0,-1))
+                                       vertices[i].Pos.rotateXZBy(-90);
+                               if(dir == v3s16(0,-1,0))
+                                       vertices[i].Pos.rotateXYBy(-90);
+                               if(dir == v3s16(0,1,0))
+                                       vertices[i].Pos.rotateXYBy(90);
+
+                               vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
+                       }
+
+                       // Set material
+                       video::SMaterial material;
+                       material.setFlag(video::EMF_LIGHTING, false);
+                       material.setFlag(video::EMF_BACK_FACE_CULLING, false);
+                       material.setFlag(video::EMF_BILINEAR_FILTER, false);
+                       material.setFlag(video::EMF_FOG_ENABLE, true);
+                       //material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
+                       material.MaterialType
+                                       = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
+
+                       material.setTexture(0, 
+                                       g_texturesource->getTextureRaw("sign_wall.png"));
+
+                       u16 indices[] = {0,1,2,2,3,0};
+                       // Add to mesh collector
+                       collector.append(material, vertices, 4, indices, 6);
+               }
+               /*
+                       Add flowing water to mesh
+               */
+               else if(n.d == CONTENT_WATER)
+               {
+                       bool top_is_water = false;
+                       MapNode ntop = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y+1,z));
+                       if(ntop.d == CONTENT_WATER || ntop.d == CONTENT_WATERSOURCE)
+                               top_is_water = true;
+                       
+                       u8 l = 0;
+                       // Use the light of the node on top if possible
+                       if(content_features(ntop.d).param_type == CPT_LIGHT)
+                               l = decode_light(ntop.getLightBlend(data->m_daynight_ratio));
+                       // Otherwise use the light of this node (the water)
+                       else
+                               l = decode_light(n.getLightBlend(data->m_daynight_ratio));
+                       video::SColor c(WATER_ALPHA,l,l,l);
+                       
+                       // Neighbor water levels (key = relative position)
+                       // Includes current node
+                       core::map<v3s16, f32> neighbor_levels;
+                       core::map<v3s16, u8> neighbor_contents;
+                       core::map<v3s16, u8> neighbor_flags;
+                       const u8 neighborflag_top_is_water = 0x01;
+                       v3s16 neighbor_dirs[9] = {
+                               v3s16(0,0,0),
+                               v3s16(0,0,1),
+                               v3s16(0,0,-1),
+                               v3s16(1,0,0),
+                               v3s16(-1,0,0),
+                               v3s16(1,0,1),
+                               v3s16(-1,0,-1),
+                               v3s16(1,0,-1),
+                               v3s16(-1,0,1),
+                       };
+                       for(u32 i=0; i<9; i++)
+                       {
+                               u8 content = CONTENT_AIR;
+                               float level = -0.5 * BS;
+                               u8 flags = 0;
+                               // Check neighbor
+                               v3s16 p2 = p + neighbor_dirs[i];
+                               MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
+                               if(n2.d != CONTENT_IGNORE)
+                               {
+                                       content = n2.d;
+
+                                       if(n2.d == CONTENT_WATERSOURCE)
+                                               level = (-0.5+node_water_level) * BS;
+                                       else if(n2.d == CONTENT_WATER)
+                                               level = (-0.5 + ((float)n2.param2 + 0.5) / 8.0
+                                                               * node_water_level) * BS;
+
+                                       // Check node above neighbor.
+                                       // NOTE: This doesn't get executed if neighbor
+                                       //       doesn't exist
+                                       p2.Y += 1;
+                                       n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
+                                       if(n2.d == CONTENT_WATERSOURCE || n2.d == CONTENT_WATER)
+                                               flags |= neighborflag_top_is_water;
+                               }
+                               
+                               neighbor_levels.insert(neighbor_dirs[i], level);
+                               neighbor_contents.insert(neighbor_dirs[i], content);
+                               neighbor_flags.insert(neighbor_dirs[i], flags);
+                       }
+
+                       //float water_level = (-0.5 + ((float)n.param2 + 0.5) / 8.0) * BS;
+                       //float water_level = neighbor_levels[v3s16(0,0,0)];
+
+                       // Corner heights (average between four waters)
+                       f32 corner_levels[4];
+                       
+                       v3s16 halfdirs[4] = {
+                               v3s16(0,0,0),
+                               v3s16(1,0,0),
+                               v3s16(1,0,1),
+                               v3s16(0,0,1),
+                       };
+                       for(u32 i=0; i<4; i++)
+                       {
+                               v3s16 cornerdir = halfdirs[i];
+                               float cornerlevel = 0;
+                               u32 valid_count = 0;
+                               for(u32 j=0; j<4; j++)
+                               {
+                                       v3s16 neighbordir = cornerdir - halfdirs[j];
+                                       u8 content = neighbor_contents[neighbordir];
+                                       // Special case for source nodes
+                                       if(content == CONTENT_WATERSOURCE)
+                                       {
+                                               cornerlevel = (-0.5+node_water_level)*BS;
+                                               valid_count = 1;
+                                               break;
+                                       }
+                                       else if(content == CONTENT_WATER)
+                                       {
+                                               cornerlevel += neighbor_levels[neighbordir];
+                                               valid_count++;
+                                       }
+                                       else if(content == CONTENT_AIR)
+                                       {
+                                               cornerlevel += -0.5*BS;
+                                               valid_count++;
+                                       }
+                               }
+                               if(valid_count > 0)
+                                       cornerlevel /= valid_count;
+                               corner_levels[i] = cornerlevel;
+                       }
+
+                       /*
+                               Generate sides
+                       */
+
+                       v3s16 side_dirs[4] = {
+                               v3s16(1,0,0),
+                               v3s16(-1,0,0),
+                               v3s16(0,0,1),
+                               v3s16(0,0,-1),
+                       };
+                       s16 side_corners[4][2] = {
+                               {1, 2},
+                               {3, 0},
+                               {2, 3},
+                               {0, 1},
+                       };
+                       for(u32 i=0; i<4; i++)
+                       {
+                               v3s16 dir = side_dirs[i];
+
+                               /*
+                                       If our topside is water and neighbor's topside
+                                       is water, don't draw side face
+                               */
+                               if(top_is_water &&
+                                               neighbor_flags[dir] & neighborflag_top_is_water)
+                                       continue;
+
+                               u8 neighbor_content = neighbor_contents[dir];
+                               
+                               // Don't draw face if neighbor is not air or water
+                               if(neighbor_content != CONTENT_AIR
+                                               && neighbor_content != CONTENT_WATER)
+                                       continue;
+                               
+                               bool neighbor_is_water = (neighbor_content == CONTENT_WATER);
+                               
+                               // Don't draw any faces if neighbor is water and top is water
+                               if(neighbor_is_water == true && top_is_water == false)
+                                       continue;
+                               
+                               video::S3DVertex vertices[4] =
+                               {
+                                       /*video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,1),
+                                       video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,1),
+                                       video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
+                                       video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
+                                       video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
+                                                       pa_water1.x0(), pa_water1.y1()),
+                                       video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
+                                                       pa_water1.x1(), pa_water1.y1()),
+                                       video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
+                                                       pa_water1.x1(), pa_water1.y0()),
+                                       video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
+                                                       pa_water1.x0(), pa_water1.y0()),
+                               };
+                               
+                               /*
+                                       If our topside is water, set upper border of face
+                                       at upper border of node
+                               */
+                               if(top_is_water)
+                               {
+                                       vertices[2].Pos.Y = 0.5*BS;
+                                       vertices[3].Pos.Y = 0.5*BS;
+                               }
+                               /*
+                                       Otherwise upper position of face is corner levels
+                               */
+                               else
+                               {
+                                       vertices[2].Pos.Y = corner_levels[side_corners[i][0]];
+                                       vertices[3].Pos.Y = corner_levels[side_corners[i][1]];
+                               }
+                               
+                               /*
+                                       If neighbor is water, lower border of face is corner
+                                       water levels
+                               */
+                               if(neighbor_is_water)
+                               {
+                                       vertices[0].Pos.Y = corner_levels[side_corners[i][1]];
+                                       vertices[1].Pos.Y = corner_levels[side_corners[i][0]];
+                               }
+                               /*
+                                       If neighbor is not water, lower border of face is
+                                       lower border of node
+                               */
+                               else
+                               {
+                                       vertices[0].Pos.Y = -0.5*BS;
+                                       vertices[1].Pos.Y = -0.5*BS;
+                               }
+                               
+                               for(s32 j=0; j<4; j++)
+                               {
+                                       if(dir == v3s16(0,0,1))
+                                               vertices[j].Pos.rotateXZBy(0);
+                                       if(dir == v3s16(0,0,-1))
+                                               vertices[j].Pos.rotateXZBy(180);
+                                       if(dir == v3s16(-1,0,0))
+                                               vertices[j].Pos.rotateXZBy(90);
+                                       if(dir == v3s16(1,0,-0))
+                                               vertices[j].Pos.rotateXZBy(-90);
+
+                                       vertices[j].Pos += intToFloat(p + blockpos_nodes, BS);
+                               }
+
+                               u16 indices[] = {0,1,2,2,3,0};
+                               // Add to mesh collector
+                               collector.append(material_water1, vertices, 4, indices, 6);
+                       }
+                       
+                       /*
+                               Generate top side, if appropriate
+                       */
+                       
+                       if(top_is_water == false)
+                       {
+                               video::S3DVertex vertices[4] =
+                               {
+                                       /*video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1),
+                                       video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,1),
+                                       video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
+                                       video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
+                                       video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
+                                                       pa_water1.x0(), pa_water1.y1()),
+                                       video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
+                                                       pa_water1.x1(), pa_water1.y1()),
+                                       video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c,
+                                                       pa_water1.x1(), pa_water1.y0()),
+                                       video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c,
+                                                       pa_water1.x0(), pa_water1.y0()),
+                               };
+                               
+                               // This fixes a strange bug
+                               s32 corner_resolve[4] = {3,2,1,0};
+
+                               for(s32 i=0; i<4; i++)
+                               {
+                                       //vertices[i].Pos.Y += water_level;
+                                       //vertices[i].Pos.Y += neighbor_levels[v3s16(0,0,0)];
+                                       s32 j = corner_resolve[i];
+                                       vertices[i].Pos.Y += corner_levels[j];
+                                       vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
+                               }
+
+                               u16 indices[] = {0,1,2,2,3,0};
+                               // Add to mesh collector
+                               collector.append(material_water1, vertices, 4, indices, 6);
+                       }
+               }
+               /*
+                       Add water sources to mesh if using new style
+               */
+               else if(n.d == CONTENT_WATERSOURCE && new_style_water)
+               {
+                       //bool top_is_water = false;
+                       bool top_is_air = false;
+                       MapNode n = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y+1,z));
+                       /*if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
+                               top_is_water = true;*/
+                       if(n.d == CONTENT_AIR)
+                               top_is_air = true;
+                       
+                       /*if(top_is_water == true)
+                               continue;*/
+                       if(top_is_air == false)
+                               continue;
+
+                       u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio));
+                       video::SColor c(WATER_ALPHA,l,l,l);
+                       
+                       video::S3DVertex vertices[4] =
+                       {
+                               /*video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1),
+                               video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,1),
+                               video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
+                               video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
+                               video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
+                                               pa_water1.x0(), pa_water1.y1()),
+                               video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
+                                               pa_water1.x1(), pa_water1.y1()),
+                               video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c,
+                                               pa_water1.x1(), pa_water1.y0()),
+                               video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c,
+                                               pa_water1.x0(), pa_water1.y0()),
+                       };
+
+                       for(s32 i=0; i<4; i++)
+                       {
+                               vertices[i].Pos.Y += (-0.5+node_water_level)*BS;
+                               vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
+                       }
+
+                       u16 indices[] = {0,1,2,2,3,0};
+                       // Add to mesh collector
+                       collector.append(material_water1, vertices, 4, indices, 6);
+               }
+               /*
+                       Add leaves if using new style
+               */
+               else if(n.d == CONTENT_LEAVES && new_style_leaves)
+               {
+                       /*u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio));*/
+                       u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio)));
+                       video::SColor c(255,l,l,l);
+
+                       for(u32 j=0; j<6; j++)
+                       {
+                               video::S3DVertex vertices[4] =
+                               {
+                                       /*video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c, 0,1),
+                                       video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c, 1,1),
+                                       video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c, 1,0),
+                                       video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c, 0,0),*/
+                                       video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c,
+                                               pa_leaves1.x0(), pa_leaves1.y1()),
+                                       video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c,
+                                               pa_leaves1.x1(), pa_leaves1.y1()),
+                                       video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c,
+                                               pa_leaves1.x1(), pa_leaves1.y0()),
+                                       video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c,
+                                               pa_leaves1.x0(), pa_leaves1.y0()),
+                               };
+
+                               if(j == 0)
+                               {
+                                       for(u16 i=0; i<4; i++)
+                                               vertices[i].Pos.rotateXZBy(0);
+                               }
+                               else if(j == 1)
+                               {
+                                       for(u16 i=0; i<4; i++)
+                                               vertices[i].Pos.rotateXZBy(180);
+                               }
+                               else if(j == 2)
+                               {
+                                       for(u16 i=0; i<4; i++)
+                                               vertices[i].Pos.rotateXZBy(-90);
+                               }
+                               else if(j == 3)
+                               {
+                                       for(u16 i=0; i<4; i++)
+                                               vertices[i].Pos.rotateXZBy(90);
+                               }
+                               else if(j == 4)
+                               {
+                                       for(u16 i=0; i<4; i++)
+                                               vertices[i].Pos.rotateYZBy(-90);
+                               }
+                               else if(j == 5)
+                               {
+                                       for(u16 i=0; i<4; i++)
+                                               vertices[i].Pos.rotateYZBy(90);
+                               }
+
+                               for(u16 i=0; i<4; i++)
+                               {
+                                       vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
+                               }
+
+                               u16 indices[] = {0,1,2,2,3,0};
+                               // Add to mesh collector
+                               collector.append(material_leaves1, vertices, 4, indices, 6);
+                       }
+               }
+               /*
+                       Add glass
+               */
+               else if(n.d == CONTENT_GLASS)
+               {
+                       u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio)));
+                       video::SColor c(255,l,l,l);
+
+                       for(u32 j=0; j<6; j++)
+                       {
+                               video::S3DVertex vertices[4] =
+                               {
+                                       video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c,
+                                               pa_glass.x0(), pa_glass.y1()),
+                                       video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c,
+                                               pa_glass.x1(), pa_glass.y1()),
+                                       video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c,
+                                               pa_glass.x1(), pa_glass.y0()),
+                                       video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c,
+                                               pa_glass.x0(), pa_glass.y0()),
+                               };
+
+                               if(j == 0)
+                               {
+                                       for(u16 i=0; i<4; i++)
+                                               vertices[i].Pos.rotateXZBy(0);
+                               }
+                               else if(j == 1)
+                               {
+                                       for(u16 i=0; i<4; i++)
+                                               vertices[i].Pos.rotateXZBy(180);
+                               }
+                               else if(j == 2)
+                               {
+                                       for(u16 i=0; i<4; i++)
+                                               vertices[i].Pos.rotateXZBy(-90);
+                               }
+                               else if(j == 3)
+                               {
+                                       for(u16 i=0; i<4; i++)
+                                               vertices[i].Pos.rotateXZBy(90);
+                               }
+                               else if(j == 4)
+                               {
+                                       for(u16 i=0; i<4; i++)
+                                               vertices[i].Pos.rotateYZBy(-90);
+                               }
+                               else if(j == 5)
+                               {
+                                       for(u16 i=0; i<4; i++)
+                                               vertices[i].Pos.rotateYZBy(90);
+                               }
+
+                               for(u16 i=0; i<4; i++)
+                               {
+                                       vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
+                               }
+
+                               u16 indices[] = {0,1,2,2,3,0};
+                               // Add to mesh collector
+                               collector.append(material_glass, vertices, 4, indices, 6);
+                       }
+               }
+
+
+
+       }
+
+       /*
+               Add stuff from collector to mesh
+       */
+       
+       scene::SMesh *mesh_new = NULL;
+       mesh_new = new scene::SMesh();
+       
+       collector.fillMesh(mesh_new);
+
+       /*
+               Do some stuff to the mesh
+       */
+
+       mesh_new->recalculateBoundingBox();
+
+       /*
+               Delete new mesh if it is empty
+       */
+
+       if(mesh_new->getMeshBufferCount() == 0)
+       {
+               mesh_new->drop();
+               mesh_new = NULL;
+       }
+
+       if(mesh_new)
+       {
+#if 0
+               // Usually 1-700 faces and 1-7 materials
+               std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces "
+                               <<"and uses "<<mesh_new->getMeshBufferCount()
+                               <<" materials (meshbuffers)"<<std::endl;
+#endif
+
+               // Use VBO for mesh (this just would set this for ever buffer)
+               // This will lead to infinite memory usage because or irrlicht.
+               //mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
+
+               /*
+                       NOTE: If that is enabled, some kind of a queue to the main
+                       thread should be made which would call irrlicht to delete
+                       the hardware buffer and then delete the mesh
+               */
+       }
+
+       return mesh_new;
+       
+       //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
+}
+
+#endif // !SERVER
+
+/*
+       MapBlock
+*/
+
+MapBlock::MapBlock(NodeContainer *parent, v3s16 pos, bool dummy):
+               m_parent(parent),
+               m_pos(pos),
+               changed(true),
+               is_underground(false),
+               m_lighting_expired(true),
+               m_day_night_differs(false),
+               //m_not_fully_generated(false),
+               m_objects(this)
+{
+       data = NULL;
+       if(dummy == false)
+               reallocate();
+       
+       //m_spawn_timer = -10000;
+
+#ifndef SERVER
+       m_mesh_expired = false;
+       mesh_mutex.Init();
+       mesh = NULL;
+       m_temp_mods_mutex.Init();
+#endif
+}
+
+MapBlock::~MapBlock()
+{
+#ifndef SERVER
+       {
+               JMutexAutoLock lock(mesh_mutex);
+               
+               if(mesh)
+               {
+                       mesh->drop();
+                       mesh = NULL;
+               }
+       }
+#endif
+
+       if(data)
+               delete[] data;
+}
 
-                       u16 indices[] = {0,1,2,2,3,0};
-                       buf->append(vertices, 4, indices, 6);
+bool MapBlock::isValidPositionParent(v3s16 p)
+{
+       if(isValidPosition(p))
+       {
+               return true;
+       }
+       else{
+               return m_parent->isValidPosition(getPosRelative() + p);
+       }
+}
 
-                       // Set material
-                       buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
-                       buf->getMaterial().setFlag(video::EMF_BACK_FACE_CULLING, false);
-                       buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
-                       //buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
-                       buf->getMaterial().MaterialType
-                                       = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
-                       if(dir == v3s16(0,-1,0))
-                               buf->getMaterial().setTexture(0,
-                                               g_texturecache.get("torch_on_floor"));
-                       else if(dir == v3s16(0,1,0))
-                               buf->getMaterial().setTexture(0,
-                                               g_texturecache.get("torch_on_ceiling"));
-                       // For backwards compatibility
-                       else if(dir == v3s16(0,0,0))
-                               buf->getMaterial().setTexture(0,
-                                               g_texturecache.get("torch_on_floor"));
-                       else
-                               buf->getMaterial().setTexture(0, g_texturecache.get("torch"));
+MapNode MapBlock::getNodeParent(v3s16 p)
+{
+       if(isValidPosition(p) == false)
+       {
+               return m_parent->getNode(getPosRelative() + p);
+       }
+       else
+       {
+               if(data == NULL)
+                       throw InvalidPositionException();
+               return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
+       }
+}
 
-                       // Add to mesh
-                       mesh_new->addMeshBuffer(buf);
-                       buf->drop();
-               }
+void MapBlock::setNodeParent(v3s16 p, MapNode & n)
+{
+       if(isValidPosition(p) == false)
+       {
+               m_parent->setNode(getPosRelative() + p, n);
+       }
+       else
+       {
+               if(data == NULL)
+                       throw InvalidPositionException();
+               data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X] = n;
        }
+}
 
-       /*
-               Do some stuff to the mesh
-       */
+MapNode MapBlock::getNodeParentNoEx(v3s16 p)
+{
+       if(isValidPosition(p) == false)
+       {
+               try{
+                       return m_parent->getNode(getPosRelative() + p);
+               }
+               catch(InvalidPositionException &e)
+               {
+                       return MapNode(CONTENT_IGNORE);
+               }
+       }
+       else
+       {
+               if(data == NULL)
+               {
+                       return MapNode(CONTENT_IGNORE);
+               }
+               return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
+       }
+}
 
-       mesh_new->recalculateBoundingBox();
+#ifndef SERVER
 
+#if 1
+void MapBlock::updateMesh(u32 daynight_ratio)
+{
+#if 0
        /*
-               Delete new mesh if it is empty
+               DEBUG: If mesh has been generated, don't generate it again
        */
-
-       if(mesh_new->getMeshBufferCount() == 0)
        {
-               mesh_new->drop();
-               mesh_new = NULL;
+               JMutexAutoLock meshlock(mesh_mutex);
+               if(mesh != NULL)
+                       return;
        }
+#endif
 
+       MeshMakeData data;
+       data.fill(daynight_ratio, this);
+       
+       scene::SMesh *mesh_new = makeMapBlockMesh(&data);
+       
        /*
                Replace the mesh
        */
 
+       replaceMesh(mesh_new);
+
+}
+#endif
+
+void MapBlock::replaceMesh(scene::SMesh *mesh_new)
+{
        mesh_mutex.Lock();
 
-       scene::SMesh *mesh_old = mesh;
+       //scene::SMesh *mesh_old = mesh[daynight_i];
+       //mesh[daynight_i] = mesh_new;
 
+       scene::SMesh *mesh_old = mesh;
        mesh = mesh_new;
+       setMeshExpired(false);
        
        if(mesh_old != NULL)
        {
@@ -649,15 +1694,27 @@ void MapBlock::updateMesh()
                {
                        IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
                }*/
+               
+               /*dstream<<"mesh_old->getReferenceCount()="
+                               <<mesh_old->getReferenceCount()<<std::endl;
+               u32 c = mesh_old->getMeshBufferCount();
+               for(u32 i=0; i<c; i++)
+               {
+                       scene::IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
+                       dstream<<"buf->getReferenceCount()="
+                                       <<buf->getReferenceCount()<<std::endl;
+               }*/
+
                // Drop the mesh
                mesh_old->drop();
+
                //delete mesh_old;
        }
 
        mesh_mutex.Unlock();
-       
-       //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
 }
+       
+#endif // !SERVER
 
 /*
        Propagates sunlight down through the block.
@@ -668,12 +1725,21 @@ void MapBlock::updateMesh()
 
        If there is a block above, continues from it.
        If there is no block above, assumes there is sunlight, unless
-       is_underground is set.
+       is_underground is set or highest node is water.
 
        At the moment, all sunlighted nodes are added to light_sources.
-       TODO: This could be optimized.
+       - SUGG: This could be optimized
+
+       Turns sunglighted mud into grass.
+
+       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 MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources,
+               bool remove_light, bool *black_air_left,
+               bool grow_grass)
 {
        // Whether the sunlight at the top of the bottom block is valid
        bool block_below_is_valid = true;
@@ -684,17 +1750,14 @@ bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources)
        {
                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.getLight() != LIGHT_SUN)
+                               if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
                                {
-                                       /*if(is_underground)
-                                       {
-                                               no_sunlight = true;
-                                       }*/
                                        no_sunlight = true;
                                }
                        }
@@ -702,18 +1765,41 @@ bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources)
                        {
                                no_top_block = true;
                                
-                               // TODO: This makes over-ground roofed places sunlighted
+                               // NOTE: This makes over-ground roofed places sunlighted
                                // Assume sunlight, unless is_underground==true
                                if(is_underground)
                                {
                                        no_sunlight = true;
                                }
-                               
-                               // TODO: There has to be some way to allow this behaviour
-                               // As of now, it just makes everything dark.
+                               else
+                               {
+                                       MapNode n = getNode(v3s16(x, MAP_BLOCKSIZE-1, z));
+                                       if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
+                                       {
+                                               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
@@ -723,45 +1809,81 @@ bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources)
                
                        s16 y = MAP_BLOCKSIZE-1;
                        
-                       if(no_sunlight == false)
+                       // 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--)
                        {
-                               // Continue spreading sunlight downwards through transparent
-                               // nodes
-                               for(; y >= 0; y--)
+                               v3s16 pos(x, y, z);
+                               MapNode &n = getNodeRef(pos);
+                               
+                               if(current_light == 0)
                                {
-                                       v3s16 pos(x, y, z);
-                                       
-                                       MapNode &n = getNodeRef(pos);
-
-                                       if(n.sunlight_propagates())
+                                       // Do nothing
+                               }
+                               else if(current_light == LIGHT_SUN && n.sunlight_propagates())
+                               {
+                                       // Do nothing: Sunlight is continued
+                               }
+                               else if(n.light_propagates() == false)
+                               {
+                                       if(grow_grass)
                                        {
-                                               n.setLight(LIGHT_SUN);
-
-                                               light_sources.insert(pos_relative + pos, true);
-                                       }
-                                       else{
-                                               break;
+                                               bool upper_is_air = false;
+                                               try
+                                               {
+                                                       if(getNodeParent(pos+v3s16(0,1,0)).d == CONTENT_AIR)
+                                                               upper_is_air = true;
+                                               }
+                                               catch(InvalidPositionException &e)
+                                               {
+                                               }
+                                               // Turn mud into grass
+                                               if(upper_is_air && n.d == CONTENT_MUD
+                                                               && current_light == LIGHT_SUN)
+                                               {
+                                                       n.d = CONTENT_GRASS;
+                                               }
                                        }
+
+                                       // 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);
                                }
-                       }
 
-                       bool sunlight_should_go_down = (y==-1);
+                               u8 old_light = n.getLight(LIGHTBANK_DAY);
 
-                       // Fill rest with black (only transparent ones)
-                       for(; y >= 0; y--){
-                               v3s16 pos(x, y, z);
+                               if(current_light > old_light || remove_light)
+                               {
+                                       n.setLight(LIGHTBANK_DAY, current_light);
+                               }
                                
-                               MapNode &n = getNodeRef(pos);
-
-                               if(n.light_propagates())
+                               if(diminish_light(current_light) != 0)
                                {
-                                       n.setLight(0);
+                                       light_sources.insert(pos_relative + pos, true);
                                }
-                               else{
-                                       break;
+
+                               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:
 
@@ -777,10 +1899,10 @@ bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources)
                                MapNode n = getNodeParent(v3s16(x, -1, z));
                                if(n.light_propagates())
                                {
-                                       if(n.getLight() == LIGHT_SUN
+                                       if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN
                                                        && sunlight_should_go_down == false)
                                                block_below_is_valid = false;
-                                       else if(n.getLight() != LIGHT_SUN
+                                       else if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN
                                                        && sunlight_should_go_down == true)
                                                block_below_is_valid = false;
                                }
@@ -798,19 +1920,149 @@ bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources)
        return block_below_is_valid;
 }
 
+
 void MapBlock::copyTo(VoxelManipulator &dst)
 {
        v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
        VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
        
+       // Copy from data to VoxelManipulator
        dst.copyFrom(data, data_area, v3s16(0,0,0),
                        getPosRelative(), data_size);
 }
 
-/*void getPseudoObjects(v3f origin, f32 max_d,
-               core::array<DistanceSortedObject> &dest)
+void MapBlock::copyFrom(VoxelManipulator &dst)
+{
+       v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
+       VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
+       
+       // Copy from VoxelManipulator to data
+       dst.copyTo(data, data_area, v3s16(0,0,0),
+                       getPosRelative(), data_size);
+}
+
+void MapBlock::stepObjects(float dtime, bool server, u32 daynight_ratio)
+{
+       /*
+               Step objects
+       */
+       m_objects.step(dtime, server, daynight_ratio);
+
+#if 0
+       /*
+               Spawn some objects at random.
+
+               Use dayNightDiffed() to approximate being near ground level
+       */
+       if(m_spawn_timer < -999)
+       {
+               m_spawn_timer = 60;
+       }
+       if(dayNightDiffed() == true && getObjectCount() == 0)
+       {
+               m_spawn_timer -= dtime;
+               if(m_spawn_timer <= 0.0)
+               {
+                       m_spawn_timer += myrand() % 300;
+                       
+                       v2s16 p2d(
+                               (myrand()%(MAP_BLOCKSIZE-1))+0,
+                               (myrand()%(MAP_BLOCKSIZE-1))+0
+                       );
+
+                       s16 y = getGroundLevel(p2d);
+                       
+                       if(y >= 0)
+                       {
+                               v3s16 p(p2d.X, y+1, p2d.Y);
+
+                               if(getNode(p).d == CONTENT_AIR
+                                               && getNode(p).getLightBlend(daynight_ratio) <= 11)
+                               {
+                                       RatObject *obj = new RatObject(NULL, -1, intToFloat(p, BS));
+                                       addObject(obj);
+                               }
+                       }
+               }
+       }
+#endif
+
+       setChangedFlag();
+}
+
+
+void MapBlock::updateDayNightDiff()
+{
+       if(data == NULL)
+       {
+               m_day_night_differs = false;
+               return;
+       }
+
+       bool differs = false;
+
+       /*
+               Check if any lighting value differs
+       */
+       for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
+       {
+               MapNode &n = data[i];
+               if(n.getLight(LIGHTBANK_DAY) != n.getLight(LIGHTBANK_NIGHT))
+               {
+                       differs = true;
+                       break;
+               }
+       }
+
+       /*
+               If some lighting values differ, check if the whole thing is
+               just air. If it is, differ = false
+       */
+       if(differs)
+       {
+               bool only_air = true;
+               for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
+               {
+                       MapNode &n = data[i];
+                       if(n.d != CONTENT_AIR)
+                       {
+                               only_air = false;
+                               break;
+                       }
+               }
+               if(only_air)
+                       differs = false;
+       }
+
+       // Set member variable
+       m_day_night_differs = differs;
+}
+
+s16 MapBlock::getGroundLevel(v2s16 p2d)
 {
-}*/
+       if(isDummy())
+               return -3;
+       try
+       {
+               s16 y = MAP_BLOCKSIZE-1;
+               for(; y>=0; y--)
+               {
+                       //if(is_ground_content(getNodeRef(p2d.X, y, p2d.Y).d))
+                       if(content_features(getNodeRef(p2d.X, y, p2d.Y).d).walkable)
+                       {
+                               if(y == MAP_BLOCKSIZE-1)
+                                       return -2;
+                               else
+                                       return y;
+                       }
+               }
+               return -1;
+       }
+       catch(InvalidPositionException &e)
+       {
+               return -3;
+       }
+}
 
 /*
        Serialization
@@ -873,20 +2125,27 @@ void MapBlock::serialize(std::ostream &os, u8 version)
                
                if(version >= 10)
                {
-                       // Get and compress pressure
-                       SharedBuffer<u8> pressuredata(nodecount);
+                       // Get and compress param2
+                       SharedBuffer<u8> param2data(nodecount);
                        for(u32 i=0; i<nodecount; i++)
                        {
-                               pressuredata[i] = data[i].pressure;
+                               param2data[i] = data[i].param2;
                        }
-                       compress(pressuredata, os, version);
+                       compress(param2data, os, version);
                }
        }
        // All other versions (newest)
        else
        {
                // First byte
-               os.write((char*)&is_underground, 1);
+               u8 flags = 0;
+               if(is_underground)
+                       flags |= 0x01;
+               if(m_day_night_differs)
+                       flags |= 0x02;
+               if(m_lighting_expired)
+                       flags |= 0x04;
+               os.write((char*)&flags, 1);
 
                u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
 
@@ -908,10 +2167,10 @@ void MapBlock::serialize(std::ostream &os, u8 version)
                        databuf[i+nodecount] = data[i].param;
                }
 
-               // Get pressure
+               // Get param2
                for(u32 i=0; i<nodecount; i++)
                {
-                       databuf[i+nodecount*2] = data[i].pressure;
+                       databuf[i+nodecount*2] = data[i].param2;
                }
 
                /*
@@ -919,6 +2178,34 @@ void MapBlock::serialize(std::ostream &os, u8 version)
                */
 
                compress(databuf, os, version);
+               
+               /*
+                       NodeMetadata
+               */
+               if(version >= 14)
+               {
+                       if(version <= 15)
+                       {
+                               try{
+                                       std::ostringstream oss(std::ios_base::binary);
+                                       m_node_metadata.serialize(oss);
+                                       os<<serializeString(oss.str());
+                               }
+                               // This will happen if the string is longer than 65535
+                               catch(SerializationError &e)
+                               {
+                                       // Use an empty string
+                                       os<<serializeString("");
+                               }
+                       }
+                       else
+                       {
+                               std::ostringstream oss(std::ios_base::binary);
+                               m_node_metadata.serialize(oss);
+                               compressZlib(oss.str(), os);
+                               //os<<serializeLongString(oss.str());
+                       }
+               }
        }
 }
 
@@ -927,6 +2214,12 @@ void MapBlock::deSerialize(std::istream &is, u8 version)
        if(!ser_ver_supported(version))
                throw VersionMismatchException("ERROR: MapBlock format not supported");
 
+       // These have no lighting info
+       if(version <= 1)
+       {
+               setLightingExpired(true);
+       }
+
        // These have no compression
        if(version <= 3 || version == 5 || version == 6)
        {
@@ -985,7 +2278,7 @@ void MapBlock::deSerialize(std::istream &is, u8 version)
        
                if(version >= 10)
                {
-                       // Uncompress and set pressure data
+                       // Uncompress and set param2 data
                        std::ostringstream os(std::ios_base::binary);
                        decompress(is, os, version);
                        std::string s = os.str();
@@ -994,7 +2287,7 @@ void MapBlock::deSerialize(std::istream &is, u8 version)
                                                ("MapBlock::deSerialize: invalid format");
                        for(u32 i=0; i<s.size(); i++)
                        {
-                               data[i].pressure = s[i];
+                               data[i].param2 = s[i];
                        }
                }
        }
@@ -1003,9 +2296,11 @@ void MapBlock::deSerialize(std::istream &is, u8 version)
        {
                u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
 
-               u8 t8;
-               is.read((char*)&t8, 1);
-               is_underground = t8;
+               u8 flags;
+               is.read((char*)&flags, 1);
+               is_underground = (flags & 0x01) ? true : false;
+               m_day_night_differs = (flags & 0x02) ? true : false;
+               m_lighting_expired = (flags & 0x04) ? true : false;
 
                // Uncompress data
                std::ostringstream os(std::ios_base::binary);
@@ -1025,10 +2320,56 @@ void MapBlock::deSerialize(std::istream &is, u8 version)
                {
                        data[i].param = s[i+nodecount];
                }
-               // Set pressure
+               // Set param2
                for(u32 i=0; i<nodecount; i++)
                {
-                       data[i].pressure = s[i+nodecount*2];
+                       data[i].param2 = s[i+nodecount*2];
+               }
+               
+               /*
+                       NodeMetadata
+               */
+               if(version >= 14)
+               {
+                       // Ignore errors
+                       try{
+                               if(version <= 15)
+                               {
+                                       std::string data = deSerializeString(is);
+                                       std::istringstream iss(data, std::ios_base::binary);
+                                       m_node_metadata.deSerialize(iss);
+                               }
+                               else
+                               {
+                                       //std::string data = deSerializeLongString(is);
+                                       std::ostringstream oss(std::ios_base::binary);
+                                       decompressZlib(is, oss);
+                                       std::istringstream iss(oss.str(), std::ios_base::binary);
+                                       m_node_metadata.deSerialize(iss);
+                               }
+                       }
+                       catch(SerializationError &e)
+                       {
+                               dstream<<"WARNING: MapBlock::deSerialize(): Ignoring an error"
+                                               <<" while deserializing node metadata"<<std::endl;
+                       }
+               }
+       }
+       
+       /*
+               Translate nodes as specified in the translate_to fields of
+               node features
+       */
+       for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
+       {
+               MapNode &n = data[i];
+
+               MapNode *translate_to = content_features(n.d).translate_to;
+               if(translate_to)
+               {
+                       dstream<<"MapBlock: WARNING: Translating node "<<n.d<<" to "
+                                       <<translate_to->d<<std::endl;
+                       n = *translate_to;
                }
        }
 }